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:
-
Small Policy (<500 KB):
- ✅ JSON renders inline without scroll
- ✅ Copy button copies full JSON
- ✅ Section not collapsed by default
-
Large Policy (>500 KB):
- ✅ Warning badge shows
- ✅ Section collapsed by default
- ✅ Copy button still functional
-
Settings Catalog Policy:
- ✅ "Settings" tab shows table
- ✅ "JSON" tab shows full snapshot
- ✅ Tab switching works without layout breaks
-
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
- ✅ Phase 1 complete: Package installed, assets documented
- ✅ Phase 2 complete: Research findings documented
- Phase 3: Implement User Story 1 (T007-T012) - Basic JSON viewer
- Phase 4: Implement User Story 2 (T013-T015) - Tabs for Settings Catalog
- 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