re-uploading work

This commit is contained in:
2026-02-04 17:46:30 -06:00
commit 3b14c65998
1388 changed files with 381262 additions and 0 deletions

View 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.