TenantAtlas/specs/002-filament-json/quickstart.md
2025-12-14 19:56:17 +01:00

11 KiB

Quickstart: Feature 002 - Filament JSON UI

Feature: 002-filament-json
Date: 2025-12-13
Status: Implementation Guide


Installation

1. Install Package (T001) Complete

cd /Users/ahmeddarrazi/Documents/projects/TenantAtlas
./vendor/bin/sail composer require pepperfm/filament-json:^4

Result: Package installed, assets published to public/css/pepperfm/filament-json/


Usage Examples

Basic Integration (MVP - Phase 3)

Location: app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php

Option A: Simple Pretty-Print JSON (Fastest MVP)

use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\Section;

protected function getSchema(): array
{
    return [
        Section::make('Policy Details')
            ->schema([
                TextEntry::make('name'),
                TextEntry::make('platform'),
                // ... other fields
            ]),
        
        Section::make('Policy Snapshot')
            ->schema([
                TextEntry::make('snapshot')
                    ->label('JSON Configuration')
                    ->formatStateUsing(fn ($state) => 
                        $state 
                            ? '<pre class="text-xs font-mono overflow-x-auto bg-gray-50 dark:bg-gray-900 p-4 rounded-lg max-h-96 overflow-y-auto">' 
                                . htmlspecialchars(json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) 
                                . '</pre>'
                            : 'No snapshot available'
                    )
                    ->html()
                    ->columnSpanFull()
                    ->copyable()
                    ->copyableState(fn ($state) => json_encode($state, JSON_PRETTY_PRINT))
                    ->copyMessage('JSON copied to clipboard!')
                    ->helperText('Click the copy icon to copy the full JSON configuration.'),
            ])
            ->collapsible()
            ->collapsed(false),
    ];
}

Features:

  • Pretty-printed JSON with monospace font
  • Copy-to-clipboard via Filament's built-in copyable()
  • Scrollable container (max height 24rem)
  • Dark mode support
  • Collapsible section
  • Helper text for user guidance

With Tabs (Phase 4 - Settings Catalog)

use Filament\Infolists\Components\Tabs;
use Filament\Infolists\Components\Tabs\Tab;

protected function getSchema(): array
{
    return [
        Tabs::make('Policy Data')
            ->tabs([
                Tab::make('Settings')
                    ->visible(fn ($record) => $record->odatatype === 'settingsCatalogPolicy')
                    ->schema([
                        // Existing settings table component
                        ViewEntry::make('settings_table')
                            ->view('filament.infolists.entries.settings-table'),
                    ]),
                
                Tab::make('JSON')
                    ->schema([
                        TextEntry::make('snapshot')
                            ->label('Full Policy JSON')
                            ->formatStateUsing(fn ($state) => 
                                '<pre class="text-xs font-mono overflow-x-auto bg-gray-50 dark:bg-gray-900 p-4 rounded-lg max-h-96">' 
                                    . htmlspecialchars(json_encode($state, JSON_PRETTY_PRINT)) 
                                    . '</pre>'
                            )
                            ->html()
                            ->columnSpanFull()
                            ->copyable()
                            ->copyableState(fn ($state) => json_encode($state, JSON_PRETTY_PRINT)),
                    ]),
            ]),
    ];
}

Large Payload Warning (Phase 4 - T014)

use Filament\Infolists\Components\TextEntry;
use Filament\Support\Colors\Color;

protected function getSchema(): array
{
    return [
        Section::make('Policy Snapshot')
            ->schema([
                // Warning badge for large payloads
                TextEntry::make('snapshot_size')
                    ->label('Payload Size')
                    ->state(fn ($record) => strlen(json_encode($record->snapshot ?? [])))
                    ->formatStateUsing(fn ($state) => 
                        $state > 512000 
                            ? '<span class="text-warning-600 dark:text-warning-400 font-semibold">⚠️ Large payload (' . number_format($state / 1024, 0) . ' KB) - May impact performance</span>'
                            : number_format($state / 1024, 1) . ' KB'
                    )
                    ->html()
                    ->visible(fn ($record) => strlen(json_encode($record->snapshot ?? [])) > 512000),
                
                TextEntry::make('snapshot')
                    ->label('JSON Configuration')
                    ->formatStateUsing(fn ($state) => 
                        '<pre class="text-xs font-mono overflow-x-auto bg-gray-50 dark:bg-gray-900 p-4 rounded-lg max-h-96">' 
                            . htmlspecialchars(json_encode($state, JSON_PRETTY_PRINT)) 
                            . '</pre>'
                    )
                    ->html()
                    ->columnSpanFull()
                    ->copyable(),
            ])
            ->collapsed(fn ($record) => strlen(json_encode($record->snapshot ?? [])) > 512000), // Auto-collapse if large
    ];
}

Features:

  • Size detection: strlen(json_encode($record->snapshot)) > 512,000 bytes (500 KB)
  • Warning badge for large payloads
  • Auto-collapse section if payload is large
  • Formatted size display (KB)

Configuration

Payload Size Thresholds (NFR-036.1)

// In PolicyResource ViewPolicy page
const INLINE_VIEWER_MAX_BYTES = 512000; // 500 KB soft limit

protected function isLargePayload($record): bool
{
    return strlen(json_encode($record->snapshot ?? [])) > self::INLINE_VIEWER_MAX_BYTES;
}

Styling Consistency (NFR-036.5)

Match Filament section padding using Tailwind classes:

'<pre class="
    text-xs           // Small font for readability
    font-mono         // Monospace for JSON
    overflow-x-auto   // Horizontal scroll
    bg-gray-50        // Light background
    dark:bg-gray-900  // Dark mode background
    p-4               // Padding (matches Filament sections)
    rounded-lg        // Rounded corners
    max-h-96          // Max height (24rem)
    overflow-y-auto   // Vertical scroll
">'

Testing

Manual QA Checklist

# 1. Open Policy View page
php artisan serve  # or ./vendor/bin/sail up

# Navigate to: /admin/policies/{id}

Test Scenarios:

  1. Small Policy (<500 KB):

    • JSON renders inline without scroll
    • Copy button copies full JSON
    • Section not collapsed by default
  2. Large Policy (>500 KB):

    • Warning badge shows
    • Section collapsed by default
    • Copy button still functional
  3. Settings Catalog Policy:

    • "Settings" tab shows table
    • "JSON" tab shows full snapshot
    • Tab switching works without layout breaks
  4. Dark Mode:

    • Switch to dark mode
    • JSON background is dark gray
    • Text is readable (light color)

Feature Tests (Optional)

./vendor/bin/sail artisan test --filter=PolicyResourceViewTest

Test Example:

// tests/Feature/Filament/PolicyResourceViewTest.php

it('displays JSON viewer on policy view page', function () {
    $policy = Policy::factory()->create([
        'snapshot' => ['test' => 'data'],
    ]);
    
    livewire(ViewPolicy::class, ['record' => $policy->id])
        ->assertSeeHtml('<pre')
        ->assertSeeHtml('font-mono')
        ->assertSee('JSON Configuration');
});

it('shows large payload warning for policies over 500 KB', function () {
    $largeSnapshot = array_fill(0, 10000, ['key' => str_repeat('x', 100)]);
    $policy = Policy::factory()->create(['snapshot' => $largeSnapshot]);
    
    livewire(ViewPolicy::class, ['record' => $policy->id])
        ->assertSeeHtml('⚠️ Large payload')
        ->assertSeeHtml('KB');
});

Browser Tests (Pest 4)

// tests/Browser/PolicyJsonViewerTest.php

it('allows copying JSON to clipboard', function () {
    $policy = Policy::factory()->create();
    
    visit('/admin/policies/' . $policy->id)
        ->assertSee('JSON Configuration')
        ->click('[wire:click="copyJsonToClipboard"]')
        ->assertSee('JSON copied to clipboard!');
});

Deployment Notes

Assets

Assets committed to repository (12 KB total):

  • public/css/pepperfm/filament-json/filament-json-styles.css

No build steps required - assets are already published and ready.

Staging Validation

# On Staging server (Dokploy deployment)
1. Deploy via git push
2. Run migrations (if any): php artisan migrate
3. Clear cache: php artisan config:clear && php artisan view:clear
4. Test: Open /admin/policies/{id} and verify JSON viewer renders

Rollback Plan

If issues occur:

# Remove package
./vendor/bin/sail composer remove pepperfm/filament-json

# Revert ViewPolicy.php changes
git checkout HEAD -- app/Filament/Resources/PolicyResource/Pages/ViewPolicy.php

# Clear cache
php artisan config:clear

Performance Considerations

Rendering 500 KB JSON

  • Inline rendering: Browser handles JSON display natively (fast)
  • Copy action: JavaScript clipboard API (async, non-blocking)
  • No server overhead: JSON is already in $record->snapshot

Large Payload Strategy

For payloads >1 MB:

  • Auto-collapse section (requires manual expand)
  • Optional: Add download action instead of copy
use Filament\Infolists\Components\Actions\Action;

Actions\Action::make('downloadSnapshot')
    ->label('Download JSON')
    ->icon('heroicon-o-arrow-down-tray')
    ->action(function ($record) {
        return response()->streamDownload(function () use ($record) {
            echo json_encode($record->snapshot, JSON_PRETTY_PRINT);
        }, "policy-{$record->id}-snapshot.json");
    })
    ->visible(fn ($record) => strlen(json_encode($record->snapshot ?? [])) > 1048576), // >1 MB

Next Steps

  1. Phase 1 complete: Package installed, assets documented
  2. Phase 2 complete: Research findings documented
  3. Phase 3: Implement User Story 1 (T007-T012) - Basic JSON viewer
  4. Phase 4: Implement User Story 2 (T013-T015) - Tabs for Settings Catalog
  5. Phase 5: Implement User Story 3 (T016-T018) - Copy/download actions

Estimated time remaining: 3-4 hours for Phases 3-8


Troubleshooting

Issue: JSON not rendering

Solution: Verify $record->snapshot is not null:

->formatStateUsing(fn ($state) => $state ? /* render JSON */ : 'No snapshot available')

Issue: Copy button not working

Solution: Ensure ->copyable() and ->copyableState() are both set:

->copyable()
->copyableState(fn ($state) => json_encode($state, JSON_PRETTY_PRINT))

Issue: Horizontal overflow

Solution: Add overflow-x-auto class to <pre> tag:

'<pre class="overflow-x-auto">'

Status: Phase 2 Complete - Ready for Phase 3 implementation