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-wizard
happones/kinetix
claude-code
wizard
multi-step-forms
form-validation
middleware
vue-components
kinetix
step-layout

Multi-step wizards: a Wizard/Step form layout AND a standalone <KinetixWizard> page component with shadcn variants, per-step validation gating, and a route-gating middleware until the wizard is completed. Activates when building wizards, step flows, or gating access behind completion.

Install
php artisan boost:add-skill happones/kinetix

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

# Kinetix Wizard Development

## When to Apply

Activate this skill when:
- Building a multi-step form (`Wizard::make()->steps([Step::make()])`).
- Mounting `<KinetixWizard>` in a page (account setup, gated onboarding flow).
- Gating routes until a user finishes a wizard (`kinetix.wizard:<slug>`).

## Documentation

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

## Form layout

```php
use Happones\Kinetix\Forms\Components\{Wizard, Step, TextInput};

Wizard::make()
    ->variant('panels') // default | simple | vertical | panels | gradient
    ->steps([
        Step::make('Account')->schema([TextInput::make('email')->required()]),
        Step::make('Profile')->icon('user')->schema([TextInput::make('name')]),
    ]);
```

Advancing is blocked until the current step's **required** fields are filled
(server validation still runs on submit). Rendered by `KinetixFormWizard` →
`KinetixWizard`.

- **The current step is in the URL by default** (`?step=profile`, client-side
  replace): a reload/shared link reopens it, but never past an incomplete step
  (it opens the first one with empty required fields and corrects the URL).
  `Step::make(__('Plan'))->key('billing')` fixes a translated label's key;
  `->persistStepInQueryString('name'|false)` per wizard; config
  `kinetix.forms.query_string.steps`. Never inside a modal.

## Standalone component

```vue
<KinetixWizard :steps="steps" variant="gradient" slug="account-setup" @finish="...">
  <template #plan>…</template>   <!-- slot named per step.key -->
  <template #team>…</template>
</KinetixWizard>
```

- `steps: { key?, label, description?, icon?, color? }[]`, `variant`, `slug?`,
  `v-model:step`, `linear` (default true), `beforeNext(fromIndex) => bool|Promise`
  (return false to block — per-step validation).
- Content slot per `step.key` or scoped `#default="{ step, index, stepKey }"`;
  `#actions="{ next, prev, finish, isFirst, isLast, busy, current }"` overrides
  the nav bar. Events: `update:step`, `step-change`, `finish`.
- **`stepLayout`** (`stepper` variant, horizontal only; default `inline`):
  `inline` (indicator + label side by side, label hidden below `sm:`),
  `stacked` (indicator on top, label always visible, truncated), or `tooltip`
  (indicator only — label/description on hover/focus via reka `Tooltip*`
  primitives; most compact for many steps on narrow viewports). PHP:
  `Wizard::make()->stepLayout('stacked')`.
- **Per-step `color`** (`stepper` variant): `{ ..., color: 'success' }` accents
  that step's indicator once active/complete (`success|danger|warning|info|
  primary|gray`, via `statusButtonClass`); upcoming steps stay neutral
  regardless. PHP: `Step::make('Account')->color('success')`.
- The horizontal `stepper` indicator sits in its own `overflow-x-auto`
  wrapper and labels `truncate` — with many steps (5-6+) or long labels it
  scrolls internally instead of breaking the page layout, across every
  `stepLayout`/`fullWidth` combination.

## Gating middleware

```php
// config/kinetix.php
'wizards' => [
    'enabled' => env('KINETIX_WIZARDS_ENABLED', false),
    'teams'   => env('KINETIX_WIZARDS_TEAMS', false),
    'gates'   => ['account-setup' => 'account.setup'], // slug => redirect route name
],
```

```bash
php artisan vendor:publish --tag=kinetix-wizards-migrations && php artisan migrate
```

```php
Route::middleware(['auth', 'kinetix.wizard:account-setup'])->group(fn () => /* gated */);
Route::get('/welcome', SetupController::class)->name('account.setup'); // NOT gated
```

- Until completed, gated routes redirect to `gates[slug]`. The middleware
  no-ops when unauthenticated, when the slug has no configured route, or when
  already on the target route (loop-safe).
- `<KinetixWizard slug=...>` marks completion on finish (via
  `useKinetixWizard().complete`). Completion is per user (or per team). Reset via
  `WizardManager::reset($user, $slug)`. Endpoints: `GET/POST {prefix}/wizards/{slug}[/complete]`.
- **A setup that must hold** (team account setup): the gate as an array —
  `['route' => 'account-setup', 'scope' => 'team', 'completed' => [AccountSetup::class, 'done']]`.
  `scope: team` = one member completes it for the team; `completed` (callable
  array, receives `$user, $teamKey`) decides from your data, and the
  self-service endpoint then 403s, so the gate can't be opened by asking.
- The middleware remembers the page they were going to: finish the setup with
  `redirect()->intended(route('dashboard'))`. JSON requests get a 409
  `{wizard, url}`; a wizard route under `{current_team}` gets the team.

i18n `wizard_*` (en/es/fr/pt).

## UUID / ULID Host Models

This feature's migration builds `user_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 Wizard Development

When to Apply

Activate this skill when:

  • Building a multi-step form (Wizard::make()->steps([Step::make()])).
  • Mounting <KinetixWizard> in a page (account setup, gated onboarding flow).
  • Gating routes until a user finishes a wizard (kinetix.wizard:<slug>).

Documentation

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

Form layout

use Happones\Kinetix\Forms\Components\{Wizard, Step, TextInput};

Wizard::make()
    ->variant('panels') // default | simple | vertical | panels | gradient
    ->steps([
        Step::make('Account')->schema([TextInput::make('email')->required()]),
        Step::make('Profile')->icon('user')->schema([TextInput::make('name')]),
    ]);

Advancing is blocked until the current step's required fields are filled (server validation still runs on submit). Rendered by KinetixFormWizard → KinetixWizard.

  • The current step is in the URL by default (?step=profile, client-side replace): a reload/shared link reopens it, but never past an incomplete step (it opens the first one with empty required fields and corrects the URL). Step::make(__('Plan'))->key('billing') fixes a translated label's key; ->persistStepInQueryString('name'|false) per wizard; config kinetix.forms.query_string.steps. Never inside a modal.

Standalone component

<KinetixWizard :steps="steps" variant="gradient" slug="account-setup" @finish="...">
  <template #plan>…</template>   <!-- slot named per step.key -->
  <template #team>…</template>
</KinetixWizard>
  • steps: { key?, label, description?, icon?, color? }[], variant, slug?, v-model:step, linear (default true), beforeNext(fromIndex) => bool|Promise (return false to block — per-step validation).
  • Content slot per step.key or scoped #default="{ step, index, stepKey }"; #actions="{ next, prev, finish, isFirst, isLast, busy, current }" overrides the nav bar. Events: update:step, step-change, finish.
  • stepLayout (stepper variant, horizontal only; default inline): inline (indicator + label side by side, label hidden below sm:), stacked (indicator on top, label always visible, truncated), or tooltip (indicator only — label/description on hover/focus via reka Tooltip* primitives; most compact for many steps on narrow viewports). PHP: Wizard::make()->stepLayout('stacked').
  • Per-step color (stepper variant): { ..., color: 'success' } accents that step's indicator once active/complete (success|danger|warning|info| primary|gray, via statusButtonClass); upcoming steps stay neutral regardless. PHP: Step::make('Account')->color('success').
  • The horizontal stepper indicator sits in its own overflow-x-auto wrapper and labels truncate — with many steps (5-6+) or long labels it scrolls internally instead of breaking the page layout, across every stepLayout/fullWidth combination.

Gating middleware

// config/kinetix.php
'wizards' => [
    'enabled' => env('KINETIX_WIZARDS_ENABLED', false),
    'teams'   => env('KINETIX_WIZARDS_TEAMS', false),
    'gates'   => ['account-setup' => 'account.setup'], // slug => redirect route name
],
php artisan vendor:publish --tag=kinetix-wizards-migrations && php artisan migrate
Route::middleware(['auth', 'kinetix.wizard:account-setup'])->group(fn () => /* gated */);
Route::get('/welcome', SetupController::class)->name('account.setup'); // NOT gated
  • Until completed, gated routes redirect to gates[slug]. The middleware no-ops when unauthenticated, when the slug has no configured route, or when already on the target route (loop-safe).
  • <KinetixWizard slug=...> marks completion on finish (via useKinetixWizard().complete). Completion is per user (or per team). Reset via WizardManager::reset($user, $slug). Endpoints: GET/POST {prefix}/wizards/{slug}[/complete].
  • A setup that must hold (team account setup): the gate as an array — ['route' => 'account-setup', 'scope' => 'team', 'completed' => [AccountSetup::class, 'done']]. scope: team = one member completes it for the team; completed (callable array, receives $user, $teamKey) decides from your data, and the self-service endpoint then 403s, so the gate can't be opened by asking.
  • The middleware remembers the page they were going to: finish the setup with redirect()->intended(route('dashboard')). JSON requests get a 409 {wizard, url}; a wizard route under {current_team} gets the team.

i18n wizard_* (en/es/fr/pt).

UUID / ULID Host Models

This feature's migration builds user_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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console