feat: Complete Phase 1 frontend — WebSocket + Wipe feature end-to-end
Implements full-stack vertical slice for wipe management with real-time updates.
WebSocket Integration:
- useWebSocket composable with auto-reconnect (exponential backoff up to 30s)
- JWT authentication via query parameter
- Automatic connection on auth state change
- Bi-directional messaging support
- Message handler subscription pattern
- Vite dev proxy configured for WebSocket (ws: true)
Toast Notification System:
- Pinia store with convenience methods (success/error/warning/info)
- Vue component with Lucide icons and Tailwind styling
- Auto-dismiss with configurable duration (5s default, 8s for errors)
- Manual dismiss with X button
- Smooth slide-in transitions from bottom-right
- Stack multiple toasts with proper spacing
Wipe Store Implementation:
- All API methods: fetchProfiles, fetchSchedules, fetchHistory
- Trigger wipe with optimistic UI update
- Dry-run simulation endpoint
- Profile CRUD operations (create, update, delete)
- WebSocket event listeners for real-time status updates
- Toast notifications on wipe_started, wipe_completed, wipe_failed
- Automatic history refresh on completion events
- Error handling with user-facing messages
Real-time Event Flow:
1. User triggers wipe → POST /api/wipes/trigger
2. Backend publishes NATS event: corrosion.{license_id}.wipe_started
3. WebSocket forwards event to frontend
4. Wipe store updates history array
5. Toast notification shows "Wipe started"
6. Progress events update status in real-time
7. Completion event triggers success toast + history refresh
Files Created:
- frontend/src/composables/useWebSocket.ts (208 LOC)
- frontend/src/stores/toast.ts (63 LOC)
- frontend/src/components/ToastNotification.vue (47 LOC)
Files Modified:
- frontend/src/stores/wipe.ts (273 LOC, was 42 LOC — 5 TODO methods → fully implemented)
- frontend/src/App.vue (added ToastNotification component)
- frontend/vite.config.ts (enabled WebSocket proxy)
TypeScript: Strict mode, zero build errors
Frontend builds: ✅ 929ms, 45.86 kB gzip
Phase 1 Status: ~80% complete
- ✅ WebSocket/NATS real-time layer
- ✅ Wipe feature production-ready
- ⏸️ Remaining stores (plugins, chat, players) still stubbed
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
50
frontend/src/components/ToastNotification.vue
Normal file
50
frontend/src/components/ToastNotification.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { CheckCircle, XCircle, AlertCircle, Info, X } from 'lucide-vue-next'
|
||||
|
||||
const toastStore = useToastStore()
|
||||
|
||||
const iconMap = {
|
||||
success: CheckCircle,
|
||||
error: XCircle,
|
||||
warning: AlertCircle,
|
||||
info: Info,
|
||||
}
|
||||
|
||||
const colorMap = {
|
||||
success: 'bg-emerald-500 text-white',
|
||||
error: 'bg-red-500 text-white',
|
||||
warning: 'bg-amber-500 text-white',
|
||||
info: 'bg-blue-500 text-white',
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed bottom-6 right-6 z-[9999] space-y-3 max-w-sm w-full pointer-events-none">
|
||||
<TransitionGroup
|
||||
enter-active-class="transition-all duration-300"
|
||||
enter-from-class="opacity-0 translate-x-full"
|
||||
enter-to-class="opacity-100 translate-x-0"
|
||||
leave-active-class="transition-all duration-200"
|
||||
leave-from-class="opacity-100 translate-x-0"
|
||||
leave-to-class="opacity-0 translate-x-full"
|
||||
move-class="transition-all duration-300"
|
||||
>
|
||||
<div
|
||||
v-for="toast in toastStore.toasts"
|
||||
:key="toast.id"
|
||||
class="pointer-events-auto rounded-lg shadow-lg p-4 flex items-center gap-3"
|
||||
:class="colorMap[toast.type]"
|
||||
>
|
||||
<component :is="iconMap[toast.type]" class="w-5 h-5 flex-shrink-0" />
|
||||
<p class="flex-1 font-medium">{{ toast.message }}</p>
|
||||
<button
|
||||
@click="toastStore.removeToast(toast.id)"
|
||||
class="flex-shrink-0 p-1 hover:bg-white/20 rounded transition-colors"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user