# Phase 8 Validation Checklist > **Historical implementation snapshot, dated 2026-10-02 (notice added 2026-11-02).** This checklist is historical implementation evidence. It is not a canonical architecture owner, is outside the governed documentation structure and authority map (see PRR-048 in the [readiness register](implementation/readiness-register.md)), and must not be read as the current status. Its "Ready for validation" status describes 2 October 2026 only; no manual validation, STAGING deployment or production deployment is recorded here as completed. "Principal" in this document is superseded terminology for User; persisted identifiers such as `principal_id` remain unchanged until a separately authorised migration ([legacy identifiers](implementation/architecture.md#legacy-principal-identifiers)). Current implementation evidence is in [implementation status](implementation/status.md); unresolved readiness items are in the [readiness register](implementation/readiness-register.md). **Objective**: Verify all dashboard UI enhancements (drag-drop, resize, persistence) work correctly end-to-end. **Date**: 2026-10-02 **Status**: Ready for validation --- ## Features to Validate ### 1. Space-Making Drag-and-Drop ✓ Code Complete - [ ] **Drag initiation**: Click and hold item → opacity drops to 0.4 - [ ] **Ghost preview**: Fixed-position clone follows cursor while dragging - [ ] **Proposed position (top half)**: Cursor in top half of target → dragged item inserts before target - [ ] **Proposed position (bottom half)**: Cursor in bottom half of target → dragged item inserts after target - [ ] **Live repositioning**: Other items shift in real-time to make space (no additional markers) - [ ] **Drop acceptance**: Release mouse → `isDropAccepted = true`, new order persisted to DB - [ ] **Drag cancel**: Move cursor outside container → release → reverts to `originalOrder` - [ ] **Persistence confirmation**: Browser DevTools Network tab shows POST to `/presentations/preference/order` with 200 OK **Test Scenario**: ``` 1. Navigate to /capabilities/[id] 2. Drag middle item down to bottom position 3. Watch other items shift to show proposed position 4. Release → item stays at bottom 5. Reload page → verify item still at bottom 6. Repeat with drag outside container (release) → item reverts to original position ``` ### 2. Resize Handle (Width via Grid Span) ✓ Code Complete - [ ] **Handle visibility**: Bottom-right corner gradient triangle visible on cards - [ ] **Cursor change**: Hover over handle → cursor becomes `nwse-resize` - [ ] **Drag right**: Increases grid column span (2→3→4→6) - [ ] **Drag left**: Decreases grid column span (6→4→3→2) - [ ] **Minimum span**: Never goes below 2 columns - [ ] **Maximum span**: Never exceeds 6 columns - [ ] **Persistence**: Resize width → reload → width persisted - [ ] **Visual feedback**: Card shows `resizing` class with shadow while dragging **Test Scenario**: ``` 1. Hover over card's bottom-right corner → see gradient triangle 2. Click and drag right → span increases smoothly 3. Drag left → span decreases 4. Release → calls persistSpan() 5. Reload page → card maintains new span ``` ### 3. Resize Handle (Height via Min-Height) ✓ Code Complete - [ ] **Drag down**: Y-axis movement increases min-height - [ ] **Drag up**: Y-axis movement decreases min-height - [ ] **Minimum height**: Never goes below 12rem - [ ] **Height calculation**: Converts pixel delta to rem units (÷16) - [ ] **Persistence**: Resize height → reload → height persisted - [ ] **Effective height**: Backend provides `effective_height` in rem from database **Test Scenario**: ``` 1. Click and drag bottom-right corner down → min-height increases 2. Drag up → min-height decreases (min 12rem) 3. Release → calls persistHeight() 4. Reload page → height remains at set size 5. Verify via F12 DevTools:
has style="min-height: XXrem" ``` ### 4. Database Persistence (All Three Endpoints) ✓ Code Complete #### Order Persistence - [ ] **Endpoint**: `POST /presentations/preference/order` - [ ] **Payload**: `{ order: [{type_id, order}, ...], capability_instance_id }` - [ ] **Database**: Creates/updates `USER_PRESENTATION_PREFERENCE_TYPE` object - [ ] **Field**: Stores in `constituent_preferences[].preferred_order` - [ ] **Response**: 200 OK with `{status: 'persisted'}` #### Span Persistence - [ ] **Endpoint**: `POST /presentations/preference/span` - [ ] **Payload**: `{ type_id, preferred_span, capability_instance_id }` - [ ] **Database**: Updates `constituent_preferences[{type_id}].preferred_span` - [ ] **Valid values**: 2, 3, 4, 6 only - [ ] **Response**: 200 OK #### Height Persistence - [ ] **Endpoint**: `POST /presentations/preference/height` - [ ] **Payload**: `{ type_id, preferred_height, capability_instance_id }` - [ ] **Database**: Updates `constituent_preferences[{type_id}].preferred_height` - [ ] **Valid values**: Integer ≥ 12 (rem units) - [ ] **Response**: 200 OK **Database Validation**: ```sql -- Query stored preferences (staging database only) SELECT id, value FROM objects WHERE type_id = '8c4f6b8c-3d2e-4f10-8a11-223344556692' AND business_id = '[business_id]' LIMIT 1 ``` Check that `value.constituent_preferences` contains: - `preferred_order` (0, 1, 2, ...) - `preferred_span` (2, 3, 4, or 6) - `preferred_height` (≥ 12) ### 5. Preference Service Loading ✓ Code Complete - [ ] **Service**: `PresentationPreferenceService::enhanceConstituentsWithPreferences()` - [ ] **Loads from**: `USER_PRESENTATION_PREFERENCE_TYPE` objects in database - [ ] **Merges**: Default values (span=2, order=index, height=12) with stored preferences - [ ] **Sort order**: Returns constituents sorted by `preferred_order` - [ ] **Backwards compatible**: Missing preferences default to system defaults **Test Flow**: ``` 1. Resize and reorder items 2. Close browser (or navigate away) 3. Return to page 4. Verify: Order, span, and height all restored from database ``` ### 6. Backend Calculation of Effective Height ✓ Code Complete - [ ] **Service**: `PersistedCapabilityProjector::applyLayout()` - [ ] **Calculation**: `effective_height = max(12, preferred_height)` - [ ] **Template receives**: `effective_height` in rem units - [ ] **Minimum enforced**: Never less than 12rem (at backend) - [ ] **Dynamic rendering**: Template uses `min-height: rem` **Verification**: ```php // In capability.php template style="min-height: rem" ``` ### 7. Template Dynamic Styling ✓ Code Complete - [ ] **Span**: `style="grid-column: span "` - [ ] **Height**: `style="min-height: rem"` - [ ] **Position**: `position: relative` (for resize handle absolute positioning) - [ ] **Draggable**: `draggable="true"` on article element - [ ] **Data attributes**: `data-preferred-span`, `data-preferred-height` populated correctly --- ## End-to-End Test Flow ### Scenario A: Drag-Drop + Persistence + Reload ``` 1. Load /capabilities/[id] 2. Note current order: [A, B, C, D, E] 3. Drag C to bottom → order becomes [A, B, D, E, C] 4. Verify Network tab: POST /presentations/preference/order → 200 OK 5. Reload page (F5) 6. Verify order is still [A, B, D, E, C] 7. Undo: Drag C back to original position → order [A, B, C, D, E] 8. Reload → confirm reverted ``` ### Scenario B: Resize Width + Persistence + Reload ``` 1. Load /capabilities/[id] 2. Note item A span: 2 columns 3. Drag bottom-right corner right → span increases to 4 4. Verify Network tab: POST /presentations/preference/span → 200 OK 5. Reload page (F5) 6. Verify item A still spans 4 columns 7. Drag corner left → span decreases to 2 8. Reload → confirm back to 2 ``` ### Scenario C: Resize Height + Persistence + Reload ``` 1. Load /capabilities/[id] 2. Note item B min-height: 12rem 3. Drag bottom-right corner down → height increases to 24rem 4. Verify Network tab: POST /presentations/preference/height → 200 OK 5. Reload page (F5) 6. Verify item B still at 24rem height 7. Drag corner up → height decreases to 12rem 8. Reload → confirm back to 12rem ``` ### Scenario D: Cancel Drag (No Persistence) ``` 1. Load /capabilities/[id] 2. Start dragging item A 3. Move to different position (A shows moving) 4. Move cursor outside container while still dragging 5. Release mouse 6. Verify: Item A reverts to original position 7. Verify Network tab: No POST request sent ``` --- ## Database State Validation (Staging Only) **NEVER run these queries against production. Use staging database only.** ```sql -- Check if preferences exist for current user/business SELECT * FROM objects WHERE type_id = '8c4f6b8c-3d2e-4f10-8a11-223344556692' AND business_id = '[your_business_id]' LIMIT 1; -- Should see JSON like: -- { -- "principal_id": "...", -- "business_id": "...", -- "capability_instance_id": "...", -- "constituent_preferences": [ -- { -- "type_id": "...", -- "preferred_order": 0, -- "preferred_span": 4, -- "preferred_height": 18 -- }, -- ... -- ] -- } ``` --- ## Browser Console Validation **Open DevTools Console (F12) and verify these logs**: ### On Page Load ``` Dashboard: Initializing (capability: [uuid]) Dashboard: Initialization complete ``` ### On Drag Start ``` Drag started on: [type_id] ``` ### During Drag Over (should log continuously) ``` Proposed position: dragged item shown at new location ``` ### On Drop ``` Persisting order: [array of type_id + order] Order persistence response: 200 Order persisted: {status: "persisted"} ``` ### On Drag Cancel (outside container) ``` Drag cancelled - reverted to original order Drag ended ``` ### On Resize ``` Resize started: [type_id] size: 2 Resizing: 3 columns, 18.0 rem height Span persistence response: 200 Span persisted: {status: "persisted"} Height persistence response: 200 Height persisted: {status: "persisted"} Resize ended: [type_id] 3 columns ``` --- ## Known Constraints 1. **Minimum Span**: 2 columns (hard limit, never below) 2. **Maximum Span**: 6 columns (grid has exactly 6 columns on desktop) 3. **Minimum Height**: 12rem (approximately 192px) 4. **Viewport Responsiveness**: - Desktop (≥1200px): 6 columns - Tablet (721-1199px): 4 columns - Mobile (≤720px): 2 columns 5. **Grid Gap**: 1rem (spacing between items remains constant) --- ## Sign-Off Criteria | Criterion | Met? | |-----------|------| | Drag-drop shows proposed position in real-time | [ ] | | Resizing works for both width and height | [ ] | | All persistence endpoints return 200 OK | [ ] | | Preferences survive page reload | [ ] | | Cancel drag reverts to original order | [ ] | | No console errors or warnings | [ ] | | Database stores all three preference types | [ ] | | Backend calculation matches frontend resize | [ ] | | Responsive grid maintains correct column count | [ ] | | All interactions persist to staging database | [ ] | --- ## Deployment Readiness **Phase 8 is COMPLETE when**: - ✅ All code changes committed to `main` - ✅ All validation checklist items pass - ✅ No regressions in existing Phase 7 functionality - ✅ Database schema supports all preference types - ✅ Staging deployment tested and verified - ✅ No breaking changes to user data model **Next Phase**: Phase 9 (Relationship Instantiation) - deferred per project plan