Advanced

Troubleshooting

Diagnose and fix the most common Primix integration issues: blank builder, asset 404s, LiVue conflicts.

Primix Troubleshooting

Common symptoms, root causes, and fixes for the Primix admin panel framework.


1. Builder iframe blank

Symptom: The visual builder canvas loads but the iframe stays white or shows a 404 console error.

Cause: Tagixo's published assets are missing or stale under public/vendor/tagixo/. The iframe sources builder.js and related chunks from that directory — Vite is not involved in serving them.

Fix:

php artisan vendor:publish --tag=tagixo-assets --force

Re-run this after every plugin version bump. Commit the updated public/vendor/tagixo/ directory — the production Dockerfile runs composer install --no-scripts so it cannot auto-publish at build time.

Note: Do not add a Vite alias or HMR entry for builder assets. They are static vendor files, not part of the app's JS bundle.


2. Save silently fails

Symptom: Clicking "Save" in the builder or a Primix resource produces no error but the change is not persisted.

Cause: A catch block somewhere in the save pipeline swallows the exception and returns a truthy response. The frontend receives a 200 and assumes success.

Fix: saveStructure() (and any method it delegates to) must let exceptions propagate. Use the tagixo:save-error event to surface failures to the UI:

// WRONG — swallows the exception
try {
    $this->saveStructure($payload);
} catch (\Exception $e) {
    // silent
}

// CORRECT — let it propagate; the framework emits tagixo:save-error
$this->saveStructure($payload);

The InteractsWithVisualBuilder trait must NOT wrap its internal calls in a bare catch. If you need to handle a specific error (e.g. validation), re-throw any \Exception you did not explicitly expect.

Warning: Never add a catch-all inside InteractsWithVisualBuilder methods — it is the wrong scope. Handle errors at the controller level only.


3. Modules palette empty

Symptom: The "Add module" palette in the builder shows no modules (or only the built-in ones).

Cause: A context mismatch between the module's registered context and the active builder context. Module registration uses an exact-string comparison — a module registered for "page" will not appear in a "mail" builder and vice versa.

Context reference:

Builder context string Used for
page Standard public pages
mail Email template builder
popup Popup builder

Fix: Verify that each custom module is registered with the correct context string:

// Registers only for the page builder
TgxModule::register('my-module', MyModule::class, context: 'page');

// Registers for all contexts
TgxModule::register('my-module', MyModule::class);

If the context argument is omitted, the module appears in all contexts. If you pass an incorrect string, the module is silently excluded.


4. LiVue component not found

Symptom: A Vue component used inside a Primix (LiVue-powered) page throws [Vue warn]: Failed to resolve component: my-component.

Cause: The component was not registered with the LiVue plugin before mount. LiVue manages its own Vue app instance — components registered on a separate app instance (e.g. a Livewire-side global) are not visible inside the LiVue context.

Important: LiVue and Livewire are separate systems. Do not confuse Livewire's Alpine/JS bridge with LiVue's Vue 3 app.

Fix: Register the component via the LiVue plugin API before the app boots:

// resources/js/app.js (or equivalent LiVue entry)
import MyComponent from './components/MyComponent.vue'

livue.component('my-component', MyComponent)

If the component is in a package, ensure the package's service provider calls LiVue::component() in its boot() method and that the provider is loaded before the page renders.


5. CSS stripped after LiVue morph

Symptom: Custom styles injected into the <head> disappear after navigating between LiVue pages (soft navigation / morphing).

Cause: LiVue's morph algorithm replaces the <head> diff, removing <style> tags that were injected dynamically by JavaScript after the initial load.

Fix: Use PrimixFormStyles::scriptFrom() to inject styles as an inline <script> that re-applies itself on every morph cycle:

use Primix\Support\PrimixFormStyles;

// In a Blade view or render hook:
{!! PrimixFormStyles::scriptFrom($css) !!}

This emits a <script> block that injects the CSS into <head> and re-runs after each LiVue navigation. Do not use a plain <style> tag or a bare document.head.appendChild() call — both will be stripped on the next morph.


6. Media gallery not opening

Symptom: Clicking a media picker field in a Primix form does nothing; the gallery modal never appears.

Cause: The media gallery is an opt-in feature. Unless withMediaGallery() is called on the Primix panel or resource, the necessary JS/modal is not booted.

Fix: Call withMediaGallery() when defining the panel:

// app/Providers/AdminPanelProvider.php
use Primix\Panel;

Panel::make()
    ->withMediaGallery()
    // ...

Or, if the gallery should only be available on specific resources, call it on the resource's panel() configuration. Without this opt-in, MediaGalleryPickerField renders a button that has no bound event listener.


7. Builder page 403

Symptom: Navigating to a builder page (e.g. /admin/pages/1/edit) returns a 403 Forbidden.

Possible causes and fixes:

a) Policy / canAccess() returning false Primix evaluates canAccess() on the resource. Check the resource class:

public static function canAccess(): bool
{
    return auth()->user()?->isAdmin() ?? false;
}

b) Guard mismatch If the admin panel uses the admin guard but the user authenticated via the web guard, auth()->user() returns null inside Primix. Ensure the panel guard and the login form guard match.

c) Stale route cache A cached route may point to an old controller or middleware. Clear it:

php artisan route:clear
php artisan optimize:clear

d) Octane cached state With Octane (Swoole), middleware and service providers can carry stale state between requests. After any policy or provider change:

php artisan octane:reload

8. App form modules not showing

Symptom: Custom form modules registered by the application do not appear in the form builder palette.

Cause: enableAppForms() is being called before the Tagixo plugin finishes booting, or it is called more than once (the second call resets the registry).

Fix: Call enableAppForms() inside a booted callback, after all service providers have run:

// app/Providers/AppServiceProvider.php
use Ccast\Tagixo\Facades\Tagixo;

public function boot(): void
{
    $this->app->booted(function () {
        Tagixo::enableAppForms();
        Tagixo::registerFormModule('my-form-module', MyFormModule::class);
    });
}

Do not call registerFormModule() before enableAppForms() — modules registered before the app forms registry is initialised will be silently discarded.

Do not call enableAppForms() twice (e.g. once in AppServiceProvider and once in AdminPanelProvider) — the second call resets the registry and discards all previously registered modules.


9. Notifications not showing

Symptom: A $dispatch('notify', ...) call inside a Primix Livewire component produces no visible notification.

Cause: Primix uses LiVue's PrimixNotification system, not Filament's $dispatch('notify') event. The two systems share a similar API surface but listen on different event buses.

Fix: Use PrimixNotification::dispatch() (server-side) or the LiVue notification composable (client-side):

// Server-side (from a Livewire component or action)
use Primix\Notifications\PrimixNotification;

PrimixNotification::dispatch('Record saved.', type: 'success');
// Client-side (inside a LiVue component)
import { useNotification } from '@primix/support'

const notify = useNotification()
notify.success('Record saved.')

Do not use Filament's $this->notify() or $dispatch('notify') in a Primix context — Filament's notification listener is not present unless the Filament panel provider is also loaded.

SDK version note: PrimixNotification::dispatch() was added in primix/primix 1.1.x. If the method is not found, update the package.