re-uploading work
This commit is contained in:
311
work-summary/auto-populate-ref-from-label.md
Normal file
311
work-summary/auto-populate-ref-from-label.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# 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
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="px-3 py-2 bg-gray-100 border rounded-lg">
|
||||
{selectedPack?.ref || "pack"}.
|
||||
</span>
|
||||
<input
|
||||
value={localRef}
|
||||
onChange={(e) => setLocalRef(e.target.value)}
|
||||
placeholder="e.g., notify_on_error"
|
||||
/>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Build Verification
|
||||
|
||||
✅ TypeScript compilation successful
|
||||
✅ Vite production build successful
|
||||
✅ Bundle size: 485.35 kB (gzip: 134.47 kB)
|
||||
✅ Test suite: 35+ tests passing
|
||||
✅ No console errors or warnings
|
||||
|
||||
## Files Created
|
||||
|
||||
1. `attune/web/src/lib/format-utils.ts` - Utility functions
|
||||
2. `attune/web/src/lib/format-utils.test.ts` - Test suite (35+ tests)
|
||||
3. `attune/work-summary/auto-populate-ref-from-label.md` - This document
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. `attune/web/src/components/forms/PackForm.tsx`
|
||||
- Reordered fields (label first, ref second)
|
||||
- Added auto-population onBlur handler
|
||||
- Updated help text
|
||||
|
||||
2. `attune/web/src/components/forms/RuleForm.tsx`
|
||||
- Reordered fields (label first, ref second)
|
||||
- Changed ref to localRef state management
|
||||
- Added pack-prefixed ref UI with split input
|
||||
- Added auto-population onBlur handler
|
||||
- Updated API submission to combine refs
|
||||
|
||||
## Future Enhancements (Optional)
|
||||
|
||||
1. **Real-Time Preview**: Show ref preview as user types label
|
||||
2. **Validation Indicator**: Show checkmark when ref is valid
|
||||
3. **Duplicate Detection**: Warn if ref already exists in pack
|
||||
4. **Custom Format Rules**: Allow pack-specific ref formatting rules
|
||||
5. **Action/Trigger Forms**: Apply same pattern when forms are created
|
||||
6. **Sensor Forms**: Apply same pattern when forms are created
|
||||
7. **Bulk Import**: Auto-generate refs for CSV/JSON imports
|
||||
|
||||
## Documentation Impact
|
||||
|
||||
The feature is self-documenting through:
|
||||
- Help text indicating auto-population
|
||||
- Visual split for pack-prefixed refs
|
||||
- Placeholder text showing expected format
|
||||
- Non-editable pack prefix showing structure
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
✅ Label to ref conversion works correctly
|
||||
✅ Auto-population triggers on label blur
|
||||
✅ Doesn't overwrite existing ref values
|
||||
✅ Disabled in edit mode
|
||||
✅ Pack prefix shows correctly for rules
|
||||
✅ Local ref input works independently
|
||||
✅ Full ref constructed correctly on submit
|
||||
✅ Empty label doesn't populate ref
|
||||
✅ Special characters converted properly
|
||||
✅ Consecutive underscores collapsed
|
||||
✅ Leading/trailing underscores removed
|
||||
✅ All utility function tests passing
|
||||
|
||||
## Conclusion
|
||||
|
||||
The auto-populate ref from label feature significantly improves the user experience for creating resources in Attune. By intelligently converting human-readable labels into technical identifiers, the system reduces manual work, eliminates formatting errors, and provides a more intuitive interface. The split input design for pack-prefixed refs makes the hierarchical structure clear and prevents common mistakes.
|
||||
|
||||
**Result**: Users can now create packs, rules, and other resources faster and with fewer errors, while maintaining consistent ref formatting across the system.
|
||||
Reference in New Issue
Block a user