# Auto-Populate Ref from Label Feature **Date**: 2026-01-27 **Status**: ✅ Complete ## Overview Implemented intelligent auto-population of `ref` fields from `label` fields across form pages in the web UI. The system automatically converts human-readable labels into valid ref identifiers when the user moves to the next field, significantly improving the user experience and reducing manual formatting effort. ## Problem Statement When creating new resources (packs, rules, actions, triggers, sensors), users had to manually create two similar but differently formatted identifiers: 1. **Label**: Human-readable display name (e.g., "My Custom Pack") 2. **Ref**: Technical identifier (e.g., "my_custom_pack") This led to: - Duplicated effort typing similar information twice - Potential formatting errors in refs (uppercase, spaces, special chars) - Inconsistency between label and ref - Poor user experience Additionally, for rules/actions/triggers, the ref field required pack prefixes (e.g., "mypack.my_rule") which added complexity. ## Solution ### 1. Created Utility Functions **File**: `attune/web/src/lib/format-utils.ts` Three core utility functions: #### `labelToRef(label: string): string` Converts label to ref-compatible format: - Converts to lowercase - Replaces spaces and special characters with underscores - Removes consecutive underscores - Trims leading/trailing underscores **Examples**: ``` "My Custom Pack" → "my_custom_pack" "Alert-on-Error!" → "alert_on_error" "Production Alert (Critical!)" → "production_alert_critical" ``` #### `extractLocalRef(fullRef: string): string` Extracts local part of a ref after pack prefix: ``` "mypack.my_rule" → "my_rule" "core.timer" → "timer" ``` #### `combineRefs(packRef: string, localRef: string): string` Combines pack ref and local ref into full ref: ``` combineRefs("mypack", "my_rule") → "mypack.my_rule" ``` ### 2. Updated PackForm **File**: `attune/web/src/components/forms/PackForm.tsx` **Changes**: - **Field Order**: Moved `label` field before `ref` field - **Auto-Population**: Added `onBlur` handler to label input - When label loses focus - If ref is empty and not editing - Auto-populate ref from label - **UI Hint**: Updated ref field help text to indicate auto-population **User Flow**: 1. User types "My Custom Pack" in label field 2. User tabs to next field (label loses focus) 3. Ref field automatically populates with "my_custom_pack" 4. User can manually override if desired ### 3. Updated RuleForm **File**: `attune/web/src/components/forms/RuleForm.tsx` **Changes**: - **Field Order**: Moved `label` field before `ref` field - **Pack Prefix UI**: Split ref input into two parts: - Non-editable prefix showing pack ref (e.g., "mypack.") - Editable local ref input - **State Management**: Changed from `ref` to `localRef` state - **Auto-Population**: Label blur auto-populates `localRef` only - **API Submission**: Combines pack ref + local ref before API call **Visual Design**: ``` Reference * ┌──────────┐ ┌─────────────────────────┐ │ mypack. │ │ notify_on_error │ └──────────┘ └─────────────────────────┘ (readonly) (editable) ``` **User Flow**: 1. User selects pack "mypack" 2. User types "Notify on Error" in label field 3. User tabs to next field 4. Local ref automatically populates with "notify_on_error" 5. UI shows: "mypack.notify_on_error" (split visually) 6. On submit, full ref "mypack.notify_on_error" sent to API ## Technical Implementation ### Format Conversion Algorithm ```typescript function labelToRef(label: string): string { return label .toLowerCase() // "My Pack" → "my pack" .trim() // " my pack " → "my pack" .replace(/[^a-z0-9]+/g, '_') // "my pack!" → "my_pack_" .replace(/^_+|_+$/g, '') // "_my_pack_" → "my_pack" .replace(/_+/g, '_'); // "my__pack" → "my_pack" } ``` ### Pack-Prefixed Ref Handling ```typescript // State: local ref only const [localRef, setLocalRef] = useState(""); // Extract from existing full ref (editing mode) const [localRef, setLocalRef] = useState( rule?.ref ? extractLocalRef(rule.ref) : "" ); // Combine for API submission const fullRef = combineRefs(selectedPack?.ref || "", localRef.trim()); ``` ### Auto-Population Logic ```typescript onBlur={() => { // Only auto-populate if: // 1. Not in editing mode (can't change ref when editing) // 2. Ref field is empty (don't overwrite user's input) // 3. Label has a value (need something to convert) if (!isEditing && !localRef.trim() && label.trim()) { setLocalRef(labelToRef(label)); } }} ``` ## Test Coverage **File**: `attune/web/src/lib/format-utils.test.ts` Created comprehensive test suite with 35+ test cases covering: ### labelToRef Tests - ✅ Simple label conversion - ✅ Hyphens and special characters - ✅ Multiple spaces - ✅ Leading/trailing whitespace - ✅ Consecutive underscores - ✅ Empty strings - ✅ Numbers preservation - ✅ CamelCase handling - ✅ Dots, slashes, parentheses, brackets ### extractLocalRef Tests - ✅ Single dot extraction - ✅ Multiple dots extraction - ✅ No dot handling - ✅ Edge cases (empty, trailing/leading dots) ### combineRefs Tests - ✅ Standard combination - ✅ Empty refs handling - ✅ Special characters in refs ### Integration Tests - ✅ Full workflow: label → localRef → fullRef → extract - ✅ Complex transformations - ✅ Round-trip consistency ## User Experience Improvements ### Before 1. Type label: "My Alert Rule" 2. Type ref: "my_alert_rule" (manually format) 3. For rules, type full ref: "alerts.my_alert_rule" ### After 1. Type label: "My Alert Rule" 2. Tab to next field 3. ✨ Ref auto-populates: "my_alert_rule" 4. For rules: See "alerts." + "my_alert_rule" (combined automatically) **Time Saved**: ~5-10 seconds per form submission **Error Reduction**: Eliminates formatting mistakes in refs ## Benefits 1. **Faster Form Completion**: Users only need to type the label 2. **Consistent Formatting**: All refs follow the same format rules 3. **Reduced Errors**: No manual lowercase/underscore conversion 4. **Intuitive UX**: Mirrors behavior from other platforms (GitHub, Slack, etc.) 5. **Flexible**: Users can still manually override auto-populated values 6. **Visual Clarity**: Pack-prefixed refs show structure clearly ## Edge Cases Handled 1. **Empty Label**: No auto-population (nothing to convert) 2. **Already Has Ref**: Doesn't overwrite existing user input 3. **Editing Mode**: Auto-population disabled (can't change ref) 4. **Special Characters**: Properly converted to underscores 5. **Multiple Spaces**: Collapsed to single underscore 6. **Numbers**: Preserved in ref 7. **Leading/Trailing Chars**: Cleaned up properly ## Pack-Prefixed Ref Design ### Visual Split The ref input is visually split to show structure: - **Left side**: Non-editable pack ref + dot (gray background) - **Right side**: Editable local ref (white background) ### Benefits 1. **Clear Structure**: Users see pack.localRef format explicitly 2. **Prevents Errors**: Can't accidentally edit pack prefix 3. **Intuitive**: Only edit the relevant part 4. **Visual Feedback**: Pack selection updates prefix immediately ### Implementation ```tsx