20 KiB
Feature 185: Settings Catalog Readable UI - Tasks
Summary
- Total Tasks: 42
- User Stories: 3 (US-UI-04, US-UI-05, US-UI-06)
- Estimated Time: 11-15 hours
- Phases: 7
FR→Task Traceability
| FR | Description | Tasks |
|---|---|---|
| FR-185.1 | Setting Definition Resolver Service | T003, T004, T005, T006, T007 |
| FR-185.2 | Database Schema | T001, T002 |
| FR-185.3 | Snapshot Enrichment | T008, T009, T010 |
| FR-185.4 | PolicyNormalizer Enhancement | T011, T012, T013, T014 |
| FR-185.5 | Policy View UI Update | T015-T024 |
| FR-185.6 | JSON Viewer Integration | T025 |
User Story→Task Mapping
| User Story | Tasks | Success Criteria |
|---|---|---|
| US-UI-04 (Readable Settings) | T015-T020 | Display names shown, values formatted, grouped by category |
| US-UI-05 (Search/Filter) | T021, T022 | Search box works, filters settings, instant results |
| US-UI-06 (Raw JSON Access) | T023, T024, T025 | Tabs present, JSON view works, copy button functional |
Measurable Thresholds
- Definition Resolution: <500ms for batch of 50 definitions (cached)
- UI Render: <2s for policy with 200 settings
- Search Response: <200ms filter update
- Database Cache TTL: 30 days
Phase 1: Database Foundation (T001-T002)
Goal: Create database schema for caching setting definitions
-
T001 Create migration for
settings_catalog_definitionstable- Schema: id, definition_id (unique), display_name, description, help_text, category_id, ux_behavior, raw (jsonb), timestamps
- Indexes: definition_id (unique), category_id, raw (GIN)
- File:
database/migrations/2025_12_13_212126_create_settings_catalog_definitions_table.php - Implementation Note: Created migration with GIN index for JSONB, ran successfully
-
T002 Create
SettingsCatalogDefinitionEloquent model- Casts: raw → array
- Fillable: definition_id, display_name, description, help_text, category_id, ux_behavior, raw
- File:
app/Models/SettingsCatalogDefinition.php - Implementation Note: Added helper methods findByDefinitionId() and findByDefinitionIds() for efficient lookups
Phase 2: Definition Resolver Service (T003-T007)
Goal: Implement service to fetch and cache setting definitions from Graph API
User Story: US-UI-04 (foundation)
-
T003 [P] Create
SettingsCatalogDefinitionResolverservice skeleton- Constructor: inject GraphClientInterface, SettingsCatalogDefinition model
- Methods: resolve(), resolveOne(), warmCache(), clearCache()
- File:
app/Services/Intune/SettingsCatalogDefinitionResolver.php - Implementation Note: Complete service with 3-tier caching (memory → DB → Graph API)
-
T004 [P] [US1] Implement
resolve(array $definitionIds): arraymethod- Check memory cache (Laravel Cache)
- Check database cache
- Batch fetch missing from Graph API:
/deviceManagement/configurationSettings?$filter=id in (...) - Store in DB + memory cache
- Return map:
{definitionId => {displayName, description, ...}} - File:
app/Services/Intune/SettingsCatalogDefinitionResolver.php - Implementation Note: Implemented with batch request optimization and error handling
-
T005 [P] [US1] Implement
resolveOne(string $definitionId): ?arraymethod- Single definition lookup
- Same caching strategy as resolve()
- Return null if not found
- File:
app/Services/Intune/SettingsCatalogDefinitionResolver.php - Implementation Note: Wraps resolve() for single ID lookup
-
T006 [US1] Implement fallback logic for missing definitions
- Prettify definition ID:
device_vendor_msft_policy_name→ "Device Vendor Msft Policy Name" - Return fallback structure:
{displayName: prettified, description: null, ...} - File:
app/Services/Intune/SettingsCatalogDefinitionResolver.php - Implementation Note: prettifyDefinitionId() method with Str::title() conversion, isFallback flag added
- Prettify definition ID:
-
T007 [P] Implement
warmCache(array $definitionIds): voidmethod- Pre-populate cache without returning data
- Non-blocking: catch and log Graph API errors
- File:
app/Services/Intune/SettingsCatalogDefinitionResolver.php - Implementation Note: Non-blocking implementation with try/catch, logs warnings on failure
Phase 3: Snapshot Enrichment (T008-T010)
Goal: Extend PolicySnapshotService to warm definition cache after settings hydration
User Story: US-UI-04 (foundation)
-
T008 [US1] Extend
PolicySnapshotServiceto extract definition IDs- After hydrating
/configurationPolicies/{id}/settings - Extract all
settingDefinitionIdfrom settings array - Include children from
groupSettingCollectionInstance - File:
app/Services/Intune/PolicySnapshotService.php - Implementation Note: Added extractDefinitionIds() method with recursive extraction from nested children
- After hydrating
-
T009 [US1] Call SettingsCatalogDefinitionResolver::warmCache() in snapshot flow
- Pass extracted definition IDs to resolver
- Non-blocking: use try/catch for Graph API calls
- File:
app/Services/Intune/PolicySnapshotService.php - Implementation Note: Integrated warmCache() call in hydrateSettingsCatalog() after settings extraction
-
T010 [US1] Add metadata to snapshot about definition cache status
- Add to snapshot:
definitions_cached: true/false,definition_count: N - Store with snapshot data
- File:
app/Services/Intune/PolicySnapshotService.php - Implementation Note: Added definitions_cached and definition_count to metadata
- Add to snapshot:
Phase 4: PolicyNormalizer Enhancement (T011-T014)
Goal: Transform Settings Catalog snapshots into UI-ready grouped structure
User Story: US-UI-04
-
T011 [US1] Create
normalizeSettingsCatalogGrouped()method in PolicyNormalizer- Input: array $snapshot, array $definitions
- Output: array with groups[] structure
- Extract settings from snapshot
- Resolve definitions for all setting IDs
- File:
app/Services/Intune/PolicyNormalizer.php - Implementation Note: Complete method with definition resolution integration
-
T012 [US1] Implement value formatting logic
- ChoiceSettingInstance: Extract choice label from @odata.type or value
- SimpleSetting (bool): "Enabled" / "Disabled"
- SimpleSetting (int): Number formatted with separators
- SimpleSetting (string): Truncate >100 chars, add "..."
- File:
app/Services/Intune/PolicyNormalizer.php - Implementation Note: Added formatSettingsCatalogValue() method with all formatting rules
-
T013 [US1] Implement grouping logic by category
- Group settings by categoryId from definition metadata
- Fallback: Group by first segment of definition ID
- Sort groups alphabetically by title
- File:
app/Services/Intune/PolicyNormalizer.php - Implementation Note: Added groupSettingsByCategory() with fallback extraction from definition IDs
-
T014 [US1] Implement nested group flattening for groupSettingCollectionInstance
- Recursively extract children from group settings
- Maintain hierarchy in output structure
- Include parent context in child labels
- File:
app/Services/Intune/PolicyNormalizer.php - Implementation Note: Recursive walk function handles nested children and group collections
Phase 5: UI Implementation - Settings Tab (T015-T022)
Goal: Create readable Settings Catalog UI with accordion, search, and formatting
User Stories: US-UI-04, US-UI-05
-
T015 [US1] Add Tabs component to PolicyResource infolist for settingsCatalogPolicy
- Conditional rendering: only for settingsCatalogPolicy type
- Tab 1: "Settings" (default)
- Tab 2: "JSON" (existing from Feature 002)
- File:
app/Filament/Resources/PolicyResource.php - Implementation Note: Tabs already exist from Feature 002, extended Settings tab with grouped view
-
T016 [US1] Create Settings tab schema with search input
- TextInput for search/filter at top
- Placeholder: "Search settings..."
- Wire with Livewire for live filtering
- File:
app/Filament/Resources/PolicyResource.php - Implementation Note: Added search_info TextEntry (hidden for MVP), search implemented in Blade template
-
T017 [US1] Create Blade component for grouped settings accordion
- File:
resources/views/filament/infolists/entries/settings-catalog-grouped.blade.php - Props: groups (from normalizer), searchQuery
- Render accordion with Filament Section components
- Implementation Note: Complete Blade component with Filament Section integration
- File:
-
T018 [US1] Implement accordion group rendering
- Each group: Section with title + description
- Collapsible by default (first group expanded)
- Group header shows setting count
- File:
settings-catalog-grouped.blade.php - Implementation Note: Using x-filament::section with collapsible, first group expanded by default
-
T019 [US1] Implement setting row rendering within groups
- Layout: Label (bold) | Value (formatted) | Help icon
- Help icon: Tooltip with description + helpText
- Copy button for long values
- File:
settings-catalog-grouped.blade.php - Implementation Note: Flexbox layout with label, help text, value display, and Alpine.js copy button
-
T020 [US1] Add value formatting in Blade template
- Bool: Badge (Enabled/Disabled with colors)
- Int: Formatted number
- String: Truncate with "..." and expand button
- Choice: Show choice label
- File:
settings-catalog-grouped.blade.php - Implementation Note: Conditional rendering based on value type, badges for bool, monospace for int
-
T021 [US2] Implement search/filter logic in Livewire component
- Filter groups and settings by search query
- Search on display_name and value_display
- Update accordion to show only matching settings
- File:
app/Filament/Resources/PolicyResource.php(or custom Livewire component) - Implementation Note: Blade-level filtering using searchQuery prop, no Livewire component needed for MVP
-
T022 [US2] Add "No results" empty state for search
- Show message when search returns no matches
- Provide "Clear search" button
- File:
settings-catalog-grouped.blade.php - Implementation Note: Empty state with clear search button using wire:click
Phase 6: UI Implementation - Tabs & Fallback (T023-T025)
Goal: Complete tab navigation and handle non-Settings Catalog policies
User Story: US-UI-06
-
T023 [US3] Verify JSON tab still works (from Feature 002)
- Tab navigation switches correctly
- JSON viewer renders snapshot
- Copy button functional
- File:
app/Filament/Resources/PolicyResource.php
-
T024 [US3] Add fallback for policies without cached definitions
- Show info message in Settings tab: "Definitions not cached. Showing raw data."
- Display raw definition IDs with prettified labels
- Link to "View JSON" tab
- File:
settings-catalog-grouped.blade.php
-
T025 Ensure JSON viewer only renders on Policy View (not globally)
- Check existing implementation from Feature 002
- Verify pepperfm/filament-json scoped correctly
- File:
app/Filament/Resources/PolicyResource.php
Phase 7: Testing & Validation (T026-T042)
Goal: Comprehensive testing for resolver, normalizer, and UI
Unit Tests (T026-T031)
-
T026 [P] Create
SettingsCatalogDefinitionResolverTesttest file- File:
tests/Unit/SettingsCatalogDefinitionResolverTest.php - Setup: Mock GraphClientInterface, in-memory database
- File:
-
T027 [P] Test
resolve()method with batch of definition IDs- Assert: Returns map with display names
- Assert: Caches in database
- Assert: Uses cached data on second call
- File:
tests/Unit/SettingsCatalogDefinitionResolverTest.php
-
T028 [P] Test fallback logic for missing definitions
- Mock: Graph API returns 404
- Assert: Returns prettified definition ID
- Assert: No exception thrown
- File:
tests/Unit/SettingsCatalogDefinitionResolverTest.php
-
T029 [P] Create
PolicyNormalizerSettingsCatalogTesttest file- File:
tests/Unit/PolicyNormalizerSettingsCatalogTest.php - Setup: Mock definition data, sample snapshot
- File:
-
T030 [P] Test grouping logic in normalizer
- Input: Snapshot with settings from different categories
- Assert: Groups created correctly
- Assert: Groups sorted alphabetically
- File:
tests/Unit/PolicyNormalizerSettingsCatalogTest.php
-
T031 [P] Test value formatting in normalizer
- Test bool → "Enabled"/"Disabled"
- Test int → formatted number
- Test string → truncation
- Test choice → label extraction
- File:
tests/Unit/PolicyNormalizerSettingsCatalogTest.php
Feature Tests (T032-T037)
-
T032 [P] Create
PolicyViewSettingsCatalogReadableTesttest file- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php - Setup: Mock GraphClient, create test policy with Settings Catalog type
- File:
-
T033 Test Settings Catalog policy view shows tabs
- Navigate to Policy View
- Assert: Tabs component present
- Assert: "Settings" and "JSON" tabs visible
- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
-
T034 Test Settings tab shows display names (not definition IDs)
- Mock: Definitions cached
- Assert: Display names shown in UI
- Assert: Definition IDs NOT visible
- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
-
T035 Test values formatted correctly
- Mock: Settings with bool, int, string, choice values
- Assert: Bool shows "Enabled"/"Disabled"
- Assert: Int shows formatted number
- Assert: String shows truncated value
- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
-
T036 [US2] Test search/filter functionality
- Input: Type search query
- Assert: Settings list filtered
- Assert: Only matching settings shown
- Assert: Clear search resets view
- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
-
T037 Test graceful degradation for missing definitions
- Mock: Definitions not cached
- Assert: Fallback labels shown (prettified IDs)
- Assert: No broken layout
- Assert: Info message visible
- File:
tests/Feature/Filament/PolicyViewSettingsCatalogReadableTest.php
Validation & Polish (T038-T042)
-
T038 Run Pest test suite for Feature 185
- Command:
./vendor/bin/sail artisan test --filter=SettingsCatalog - Assert: All tests pass
- Fix any failures
- Command:
-
T039 Run Laravel Pint on modified files
- Command:
./vendor/bin/sail pint --dirty - Assert: No style issues
- Commit fixes
- Command:
-
T040 Review git changes for Feature 185
- Check: No changes to forbidden areas (see constitution)
- Verify: Only expected files modified
- Document: List of changed files in research.md
-
T041 Run database migration on local environment
- Command:
./vendor/bin/sail artisan migrate - Verify:
settings_catalog_definitionstable created - Check: Indexes applied correctly
- Command:
-
T042 Manual QA: Policy View with Settings Catalog policy
- Navigate to Policy View for Settings Catalog policy
- Verify: Tabs present ("Settings" and "JSON")
- Verify: Settings tab shows accordion with groups
- Verify: Display names shown (not raw IDs)
- Verify: Values formatted correctly
- Test: Search filters settings
- Test: JSON tab works
- Test: Copy buttons functional
- Test: Dark mode toggle
Dependencies & Execution Order
Sequential Dependencies
- Phase 1 → Phase 2: Database must exist before resolver can cache
- Phase 2 → Phase 3: Resolver must exist before snapshot enrichment
- Phase 2 → Phase 4: Definitions needed for normalizer
- Phase 4 → Phase 5: Normalized data structure needed for UI
- Phase 5 → Phase 7: UI must exist before feature tests
Parallel Opportunities
- Phase 2 (T003-T007): Resolver methods can be implemented in parallel
- Phase 4 (T011-T014): Normalizer sub-methods can be implemented in parallel
- Phase 5 (T015-T022): UI components can be developed in parallel after T015
- Phase 7 (T026-T031): Unit tests can be written in parallel
- Phase 7 (T032-T037): Feature tests can be written in parallel
Example Parallel Execution
Phase 2:
- Developer A: T003, T004 (resolve methods)
- Developer B: T005, T006 (resolveOne + fallback)
- Both converge for T007 (warmCache)
Phase 5:
- Developer A: T015-T017 (tabs + accordion setup)
- Developer B: T018-T020 (rendering logic)
- Both converge for T021-T022 (search functionality)
Task Complexity Estimates
| Phase | Task Count | Estimated Time | Dependencies |
|---|---|---|---|
| Phase 1: Database | 2 | ~30 min | None |
| Phase 2: Resolver | 5 | ~2-3 hours | Phase 1 |
| Phase 3: Snapshot | 3 | ~1 hour | Phase 2 |
| Phase 4: Normalizer | 4 | ~2-3 hours | Phase 2 |
| Phase 5: UI Settings | 8 | ~3-4 hours | Phase 4 |
| Phase 6: UI Tabs | 3 | ~1 hour | Phase 5 |
| Phase 7: Testing | 17 | ~3-4 hours | Phase 2-6 |
| Total | 42 | 11-15 hours |
Success Criteria Checklist
- SC-001: Admin sees human-readable setting names (not definition IDs) on Policy View (Implementation complete - requires manual verification)
- SC-002: Setting values formatted appropriately (True/False, numbers, choice labels) (Implementation complete - requires manual verification)
- SC-003: Settings grouped by category with accordion (collapsible sections) (Implementation complete - requires manual verification)
- SC-004: Search/filter works on display name and value (<200ms response) (Blade-level implementation complete - requires manual verification)
- SC-005: Raw JSON available in separate "JSON" tab (Feature 002 integration preserved)
- SC-006: Unknown settings show prettified ID fallback (no broken layout) (Implementation complete - requires manual verification)
- SC-007: Performance: <2s render for policy with 200 settings (Requires load testing)
- SC-008: Tests pass: Unit tests for resolver + normalizer (Tests not written yet)
- SC-009: Tests pass: Feature tests for UI rendering (Tests not written yet)
- SC-010: Definition resolution: <500ms for batch of 50 (cached) (Requires benchmark testing)
Constitution Compliance Evidence
| Principle | Evidence | Tasks |
|---|---|---|
| Safety-First | Read-only UI, no edit capabilities | All UI tasks |
| Immutable Versioning | Snapshot enrichment non-blocking, metadata only | T008-T010 |
| Defensive Restore | Not applicable (read-only feature) | N/A |
| Auditability | Raw JSON still accessible via tab | T023-T025 |
| Tenant-Aware | Resolver respects tenant scoping (via GraphClient) | T003-T007 |
| Graph Abstraction | Uses existing GraphClientInterface | T003-T007 |
| Spec-Driven | Full spec + plan + tasks before implementation | This document |
Risk Mitigation Tasks
- Risk: Graph API rate limiting
- Mitigation: T007 (warmCache is non-blocking), aggressive DB caching
- Risk: Definition schema changes by Microsoft
- Mitigation: T001 (raw JSONB storage), T006 (fallback logic)
- Risk: Large policies slow UI
- Mitigation: T017-T018 (accordion lazy-loading), performance tests in T042
Notes for Implementation
-
Feature 002 Dependency: Feature 185 uses tabs from Feature 002 JSON viewer implementation. Ensure Feature 002 code is stable before starting Phase 5.
-
Database Migration: Run migration early (T001) to avoid blocking later phases.
-
Graph API Endpoints: Verify access to
/deviceManagement/configurationSettingsendpoint in test environment before implementing T004. -
Testing Strategy: Write unit tests (Phase 7, T026-T031) in parallel with implementation to enable TDD workflow.
-
UI Polish: Leave time for manual QA (T042) to catch UX issues not covered by automated tests.
-
Performance Profiling: Use Laravel Telescope or Debugbar during T042 to measure actual performance vs NFR targets.
Implementation Readiness
Prerequisites:
- ✅ Feature 002 JSON viewer implemented (tabs pattern established)
- ✅ pepperfm/filament-json installed
- ✅ GraphClientInterface available
- ✅ PolicyNormalizer exists
- ✅ PolicySnapshotService exists
- ✅ PostgreSQL with JSONB support
Ready to Start: Phase 1 (Database Foundation)