Files
attune/work-summary/sessions/2025-01-30-rule-toggle-switch.md
2026-02-04 17:46:30 -06:00

187 lines
6.1 KiB
Markdown

# Rule Enable/Disable Toggle Switch Implementation
**Date:** 2025-01-30
**Status:** Complete
## Overview
Added a toggle switch to the Rule Detail page that allows authenticated users to enable or disable rules directly from the UI, with proper permission checking and visual feedback.
## Changes Made
### 1. Backend API (Already Existed)
The backend already had the necessary endpoints:
- `POST /api/v1/rules/{ref}/enable` - Enable a rule
- `POST /api/v1/rules/{ref}/disable` - Disable a rule
Both endpoints:
- Require authentication (`RequireAuth` middleware)
- Update the rule's `enabled` status in the database
- Publish RabbitMQ messages (`RuleEnabled`/`RuleDisabled`) to notify the sensor service
- Return the updated rule in the response
### 2. Frontend Hooks (`web/src/hooks/useRules.ts`)
Added two new React hooks for toggling rule status:
```typescript
// Enable rule hook
export function useEnableRule() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (ref: string) => {
const response = await RulesService.enableRule({ ref });
return response;
},
onSuccess: (_, ref) => {
queryClient.invalidateQueries({ queryKey: ["rules"] });
queryClient.invalidateQueries({ queryKey: ["rules", ref] });
},
});
}
// Disable rule hook
export function useDisableRule() {
// Similar implementation for disabling
}
```
These hooks:
- Use TanStack Query's `useMutation` for state management
- Automatically invalidate relevant queries after success to refresh the UI
- Handle API calls to the enable/disable endpoints
### 3. UI Component (`web/src/pages/rules/RulesPage.tsx`)
#### Updated Imports
- Added `useEnableRule` and `useDisableRule` hooks
- Added `useAuth` context for permission checking
#### Added State and Handler
```typescript
const { isAuthenticated } = useAuth();
const enableRule = useEnableRule();
const disableRule = useDisableRule();
const [isTogglingEnabled, setIsTogglingEnabled] = useState(false);
const handleToggleEnabled = async () => {
if (!rule?.data) return;
setIsTogglingEnabled(true);
try {
if (rule.data.enabled) {
await disableRule.mutateAsync(ruleRef);
} else {
await enableRule.mutateAsync(ruleRef);
}
} catch (err) {
console.error("Failed to toggle rule enabled status:", err);
} finally {
setIsTogglingEnabled(false);
}
};
```
#### Toggle Switch UI
Replaced the static status badge with an interactive toggle switch:
- **Toggle Switch**: Tailwind CSS-based toggle with proper styling
- **Status Label**: Shows "Enabled" (green) / "Disabled" (gray) / "Updating..." (gray)
- **Disabled State**: Toggle is disabled when:
- User is not authenticated (`!isAuthenticated`)
- Toggle operation is in progress (`isTogglingEnabled`)
- **Visual Feedback**:
- Focus ring on keyboard interaction
- Smooth transition animation
- Color change (gray → blue when enabled)
- Loading state during API call
## User Experience
### Toggle Location
The toggle switch is positioned near the top of the Rule Detail page, next to the rule title, making it easily accessible and immediately visible.
### Permission Handling
- **Authenticated Users**: Can toggle the switch freely
- **Unauthenticated Users**: Toggle is disabled (grayed out)
- **During Update**: Toggle shows "Updating..." and is disabled to prevent double-clicks
### Visual States
1. **Enabled**: Blue toggle, green "Enabled" label
2. **Disabled**: Gray toggle, gray "Disabled" label
3. **Loading**: Gray toggle (disabled), "Updating..." label
4. **No Permission**: Gray toggle (disabled), current status label
### Real-time Updates
- On successful toggle, the UI immediately updates via React Query cache invalidation
- The sensor service receives notifications via RabbitMQ and adjusts monitoring accordingly
- Rule list view automatically reflects the new status
## Technical Details
### Permission System
Currently uses simple authentication check (`isAuthenticated`):
- Any authenticated user can enable/disable rules
- Backend validates JWT token via `RequireAuth` middleware
- Future: Can be extended to check fine-grained permissions when RBAC is fully implemented
### State Management
- Uses TanStack Query for server state management
- Optimistic updates are not used (waiting for server confirmation)
- Cache invalidation ensures all views stay in sync
- Local `isTogglingEnabled` state prevents UI race conditions
### Error Handling
- Errors are logged to console
- Toggle returns to previous state on failure
- User sees the actual current state from the server
## Testing Recommendations
### Manual Testing
1. **Toggle Enabled → Disabled**:
- Navigate to an enabled rule
- Click the toggle switch
- Verify it shows "Updating..."
- Verify it changes to "Disabled" after API response
- Check that sensor service stops monitoring the trigger
2. **Toggle Disabled → Enabled**:
- Navigate to a disabled rule
- Click the toggle switch
- Verify it changes to "Enabled"
- Check that sensor service starts monitoring the trigger
3. **Permission Check**:
- Log out
- Navigate to a rule detail page
- Verify toggle is disabled
4. **Double-click Prevention**:
- Toggle a rule
- Try clicking again during update
- Verify second click is ignored
### Integration Testing
- Verify RabbitMQ messages are published correctly
- Verify sensor service receives and processes messages
- Verify database `enabled` field is updated
- Verify rule executions respect the enabled flag
## Future Enhancements
1. **Fine-grained Permissions**: Add RBAC checks for `rule:enable` and `rule:disable` permissions
2. **Optimistic Updates**: Update UI immediately, rollback on error
3. **Toast Notifications**: Show success/error messages
4. **Bulk Operations**: Add ability to enable/disable multiple rules at once
5. **Audit Logging**: Track who enabled/disabled rules and when
6. **Confirmation Dialog**: Optional confirmation for critical rules
## Dependencies
- React 19
- TanStack Query (React Query)
- Tailwind CSS
- Existing API endpoints (already implemented)
## Build Status
✅ TypeScript compilation successful
✅ Vite build successful
✅ No ESLint warnings