feat: Build final 9 admin views + swap final hero graphic
Views: Plugins, Wipes, WipeProfiles, WipeCalendar, WipeHistory, Maps, Analytics, StoreManage, ModuleStore. All 20/20 admin views now implemented. Updated hero graphic to final version. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,180 @@
|
||||
<script setup lang="ts">
|
||||
// TODO: Implement auto-wiper with schedules and manual trigger
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useWipeStore } from '@/stores/wipe'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { RefreshCw, Zap, Clock, AlertTriangle, Loader2 } from 'lucide-vue-next'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
const wipeStore = useWipeStore()
|
||||
const server = useServerStore()
|
||||
|
||||
const triggerType = ref<'map' | 'blueprint' | 'full'>('map')
|
||||
const triggerLoading = ref(false)
|
||||
const dryRunLoading = ref(false)
|
||||
|
||||
async function triggerWipe() {
|
||||
if (!confirm(`Trigger a ${triggerType.value} wipe? This cannot be undone.`)) return
|
||||
triggerLoading.value = true
|
||||
try {
|
||||
await wipeStore.triggerWipe(triggerType.value, '')
|
||||
} catch {
|
||||
// Handle error
|
||||
} finally {
|
||||
triggerLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function triggerDryRun() {
|
||||
dryRunLoading.value = true
|
||||
try {
|
||||
await wipeStore.triggerDryRun(triggerType.value, '')
|
||||
} catch {
|
||||
// Handle error
|
||||
} finally {
|
||||
dryRunLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
wipeStore.fetchSchedules()
|
||||
wipeStore.fetchHistory()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-neutral-100 mb-4">Auto-Wiper</h1>
|
||||
<p class="text-neutral-400">Configure wipe schedules and trigger manual wipes.</p>
|
||||
<div class="p-6 space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<RefreshCw class="w-5 h-5 text-oxide-500" />
|
||||
<h1 class="text-2xl font-bold text-neutral-100">Auto-Wiper</h1>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<RouterLink
|
||||
to="/wipes/profiles"
|
||||
class="px-4 py-2 text-sm font-medium text-neutral-300 bg-neutral-800 hover:bg-neutral-700 rounded-lg transition-colors"
|
||||
>
|
||||
Profiles
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/wipes/calendar"
|
||||
class="px-4 py-2 text-sm font-medium text-neutral-300 bg-neutral-800 hover:bg-neutral-700 rounded-lg transition-colors"
|
||||
>
|
||||
Calendar
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
to="/wipes/history"
|
||||
class="px-4 py-2 text-sm font-medium text-neutral-300 bg-neutral-800 hover:bg-neutral-700 rounded-lg transition-colors"
|
||||
>
|
||||
History
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manual Trigger -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider mb-4">Manual Wipe</h2>
|
||||
<div class="flex items-end gap-4">
|
||||
<div>
|
||||
<label class="block text-xs text-neutral-500 mb-2">Wipe Type</label>
|
||||
<div class="flex bg-neutral-800 rounded-lg border border-neutral-700 overflow-hidden">
|
||||
<button
|
||||
v-for="opt in (['map', 'blueprint', 'full'] as const)"
|
||||
:key="opt"
|
||||
@click="triggerType = opt"
|
||||
class="px-4 py-2 text-sm font-medium transition-colors capitalize"
|
||||
:class="triggerType === opt ? 'bg-oxide-500/15 text-oxide-400' : 'text-neutral-400 hover:text-neutral-200'"
|
||||
>
|
||||
{{ opt }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="triggerDryRun"
|
||||
:disabled="dryRunLoading"
|
||||
class="flex items-center gap-2 px-4 py-2 text-sm font-medium text-neutral-300 bg-neutral-800 hover:bg-neutral-700 disabled:opacity-50 border border-neutral-700 rounded-lg transition-colors"
|
||||
>
|
||||
<Loader2 v-if="dryRunLoading" class="w-4 h-4 animate-spin" />
|
||||
<AlertTriangle v-else class="w-4 h-4" />
|
||||
Dry Run
|
||||
</button>
|
||||
<button
|
||||
@click="triggerWipe"
|
||||
:disabled="triggerLoading || server.connection?.connection_status !== 'connected'"
|
||||
class="flex items-center gap-2 px-4 py-2 text-sm font-medium bg-red-600 hover:bg-red-700 disabled:opacity-40 disabled:cursor-not-allowed text-white rounded-lg transition-colors"
|
||||
>
|
||||
<Loader2 v-if="triggerLoading" class="w-4 h-4 animate-spin" />
|
||||
<Zap v-else class="w-4 h-4" />
|
||||
Trigger Wipe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Upcoming Schedules -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider mb-4">Scheduled Wipes</h2>
|
||||
<div v-if="wipeStore.schedules.length === 0" class="text-sm text-neutral-500 py-4 text-center">
|
||||
No wipe schedules configured. Create a profile and schedule to automate wipes.
|
||||
</div>
|
||||
<div v-else class="space-y-3">
|
||||
<div
|
||||
v-for="schedule in wipeStore.schedules"
|
||||
:key="schedule.id"
|
||||
class="flex items-center justify-between p-3 bg-neutral-800/50 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<Clock class="w-4 h-4 text-neutral-500" />
|
||||
<div>
|
||||
<p class="text-sm font-medium text-neutral-200">{{ schedule.schedule_name }}</p>
|
||||
<p class="text-xs text-neutral-500">
|
||||
{{ schedule.wipe_type }} wipe · {{ schedule.cron_expression }} ({{ schedule.timezone }})
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="text-xs font-medium px-2 py-0.5 rounded-full"
|
||||
:class="schedule.is_active ? 'bg-green-500/10 text-green-400' : 'bg-neutral-700/50 text-neutral-400'"
|
||||
>
|
||||
{{ schedule.is_active ? 'Active' : 'Paused' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent History -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider">Recent Wipes</h2>
|
||||
<RouterLink to="/wipes/history" class="text-sm text-oxide-400 hover:text-oxide-300 transition-colors">
|
||||
View All
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div v-if="wipeStore.history.length === 0" class="text-sm text-neutral-500 py-4 text-center">
|
||||
No wipe history yet.
|
||||
</div>
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="wipe in wipeStore.history.slice(0, 5)"
|
||||
:key="wipe.id"
|
||||
class="flex items-center justify-between p-3 bg-neutral-800/50 rounded-lg"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm text-neutral-200">{{ wipe.wipe_type }} wipe</p>
|
||||
<p class="text-xs text-neutral-500">{{ wipe.trigger_type }} · {{ wipe.started_at ? new Date(wipe.started_at).toLocaleString() : 'Pending' }}</p>
|
||||
</div>
|
||||
<span
|
||||
class="text-xs font-medium px-2 py-0.5 rounded-full"
|
||||
:class="{
|
||||
'bg-green-500/10 text-green-400': wipe.status === 'success',
|
||||
'bg-red-500/10 text-red-400': wipe.status === 'failed' || wipe.status === 'rolled_back',
|
||||
'bg-yellow-500/10 text-yellow-400': wipe.status === 'wiping' || wipe.status === 'pre_wipe' || wipe.status === 'post_wipe',
|
||||
'bg-neutral-700/50 text-neutral-400': wipe.status === 'pending',
|
||||
}"
|
||||
>
|
||||
{{ wipe.status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user