-
- No output yet. Send a command to get started.
+
+
+
+
+ No output yet. Send a command to get started.
+
+
{{ line.text }}
-
-
{{ line.timestamp }}
-
{{ line.text }}
+
+ $
+
+
-
-
-
-
- $
-
-
-
+
+
+
diff --git a/frontend/src/views/admin/FileManagerView.vue b/frontend/src/views/admin/FileManagerView.vue
index 70b48f3..098d3b2 100644
--- a/frontend/src/views/admin/FileManagerView.vue
+++ b/frontend/src/views/admin/FileManagerView.vue
@@ -2,6 +2,7 @@
import { computed } from 'vue'
import { VueFinder, RemoteDriver } from 'vuefinder'
import { useAuthStore } from '@/stores/auth'
+import Icon from '@/components/ds/core/Icon.vue'
const auth = useAuthStore()
@@ -26,18 +27,22 @@ const finderConfig = {
-
-
-
-
File Manager
-
Browse and edit your server files
+
+
+
+
+
+
+
+
+
Server management
+
File manager
+
-
+
+
+
+
diff --git a/frontend/src/views/admin/MigrationView.vue b/frontend/src/views/admin/MigrationView.vue
index 6b5f1dd..9317521 100644
--- a/frontend/src/views/admin/MigrationView.vue
+++ b/frontend/src/views/admin/MigrationView.vue
@@ -2,8 +2,13 @@
import { ref, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'
import { useAuthStore } from '@/stores/auth'
-import { Download, Upload, FileText, Loader2 } from 'lucide-vue-next'
import { safeFileSize, safeDate } from '@/utils/formatters'
+import Panel from '@/components/ds/data/Panel.vue'
+import Button from '@/components/ds/core/Button.vue'
+import Badge from '@/components/ds/core/Badge.vue'
+import Icon from '@/components/ds/core/Icon.vue'
+import Alert from '@/components/ds/feedback/Alert.vue'
+import EmptyState from '@/components/ds/feedback/EmptyState.vue'
interface ExportRecord {
id: string
@@ -20,6 +25,8 @@ const isExporting = ref(false)
const isImporting = ref(false)
const exportType = ref<'full' | 'config_only' | 'store_only'>('full')
const uploadFile = ref
(null)
+const importError = ref(null)
+const importSuccess = ref(false)
async function fetchExports() {
exports.value = await api.get('/migration/exports')
@@ -49,6 +56,8 @@ async function importData() {
if (!confirm('Import data? This will overwrite existing configuration.')) return
isImporting.value = true
+ importError.value = null
+ importSuccess.value = false
try {
const formData = new FormData()
formData.append('file', uploadFile.value)
@@ -63,18 +72,20 @@ async function importData() {
throw new Error('Import failed')
}
- alert('Import successful')
+ importSuccess.value = true
uploadFile.value = null
} catch (err) {
- alert(err instanceof Error ? err.message : 'Import failed')
+ importError.value = err instanceof Error ? err.message : 'Import failed'
} finally {
isImporting.value = false
}
}
-function formatBytes(bytes: number): string {
- return safeFileSize(bytes)
-}
+const EXPORT_TYPE_OPTIONS = [
+ { value: 'full' as const, label: 'Full' },
+ { value: 'config_only' as const, label: 'Config only' },
+ { value: 'store_only' as const, label: 'Store only' },
+]
onMounted(() => {
fetchExports()
@@ -82,110 +93,251 @@ onMounted(() => {
-
-
-
-
-
Migration
+
+
+
-
-
-
Export Data
-
-
-
-
+
+
+
+
+
Export type
+
+ v-for="opt in EXPORT_TYPE_OPTIONS"
+ :key="opt.value"
+ type="button"
+ class="mv__seg-btn"
+ :class="exportType === opt.value && 'mv__seg-btn--active'"
+ @click="exportType = opt.value"
+ >{{ opt.label }}
-
+ @click="createExport"
+ >Export
-
+
-
-
-
-
Export History
-
-
- No exports yet.
-
-
-
+
+
+
+
+
- | Type |
- Created |
- Size |
- Actions |
+ Type |
+ Created |
+ Size |
+ Actions |
-
-
- | {{ exp.export_type.replace('_', ' ') }} |
- {{ safeDate(exp.created_at) }} |
- {{ formatBytes(exp.file_size_bytes) }} |
-
+ |
+
+ |
+ {{ exp.export_type.replace('_', ' ') }}
+ |
+ {{ safeDate(exp.created_at) }} |
+ {{ safeFileSize(exp.file_size_bytes) }} |
+
+
Download
- Preparing...
+ Preparing…
|
-
+
-
-
-
Import Data
-
-
-
+
+
+
+
Import completed successfully.
+
{{ importError }}
+
+
-
-
+
+
+
diff --git a/frontend/src/views/admin/WipeCalendarView.vue b/frontend/src/views/admin/WipeCalendarView.vue
index f27a853..480661b 100644
--- a/frontend/src/views/admin/WipeCalendarView.vue
+++ b/frontend/src/views/admin/WipeCalendarView.vue
@@ -1,7 +1,11 @@
-
-
-
-
-
Wipe Calendar
+
+
+
+
+
+
+
+
+
Auto-wiper
+
Wipe calendar
+
+
-
-
-
-
-
-
{{ monthLabel }}
-
-
-
-
+
+
+
+
+ {{ monthLabel }}
+
+
-
-
-
-
+
+
- {{ day }}
-
+ class="wc__dow"
+ >{{ day }}
+
-
+
-
- {{ day.date }}
-
-
-
- {{ day.wipeType }}
-
-
+ {{ day.date }}
+ {{ day.wipeType }}
-
+
-
-
-
Active Schedules
-
- No active schedules.
-
-
+
+
+
+
-
-
{{ schedule.schedule_name }}
-
{{ schedule.cron_expression }}
+
+
{{ schedule.schedule_name }}
+
+ {{ schedule.cron_expression }}
+ · {{ schedule.timezone }}
+
+
+
+ Next:
+
+ {{ schedule.next_scheduled_run ? new Date(schedule.next_scheduled_run).toLocaleDateString() : 'TBD' }}
+
-
- Next: {{ schedule.next_scheduled_run ? new Date(schedule.next_scheduled_run).toLocaleDateString() : 'TBD' }}
-
-
+
+
+
diff --git a/frontend/src/views/admin/WipeHistoryView.vue b/frontend/src/views/admin/WipeHistoryView.vue
index 722b493..012ab34 100644
--- a/frontend/src/views/admin/WipeHistoryView.vue
+++ b/frontend/src/views/admin/WipeHistoryView.vue
@@ -1,25 +1,24 @@
-
-
-
-
-
+
+
+
+
+
+
+
-
Wipe History
-
{{ wipeStore.history.length }} wipes recorded
+
Auto-wiper
+
Wipe history
-
-
- Refresh
-
+ @click="wipeStore.fetchHistory()"
+ >Refresh
+
+
+
+
+ {{ wipeStore.history.length }} wipe{{ wipeStore.history.length === 1 ? '' : 's' }} recorded
-
-
+
+
+
+
+
+ Loading history…
+
+
+
-
- | Type |
- Trigger |
- Status |
- Started |
- Duration |
- Map |
+
+ | Type |
+ Trigger |
+ Status |
+ Started |
+ Duration |
+ Map |
-
-
- |
- Loading history...
- No wipe history yet.
- |
-
+
- | {{ wipe.wipe_type }} |
- {{ wipe.trigger_type.replace('_', ' ') }} |
-
-
- {{ wipe.status.replace('_', ' ') }}
-
+ | {{ wipe.wipe_type }} |
+ {{ wipe.trigger_type.replace('_', ' ') }} |
+
+ {{ wipe.status.replace('_', ' ') }}
|
-
- {{ safeDate(wipe.started_at, '\u2014') }}
- |
-
- {{ duration(wipe.started_at, wipe.completed_at) }}
- |
- {{ wipe.map_used || '\u2014' }} |
+ {{ safeDate(wipe.started_at, '—') }} |
+ {{ duration(wipe.started_at, wipe.completed_at) }} |
+ {{ wipe.map_used || '—' }} |
-
+
+
+
diff --git a/frontend/src/views/platform-admin/AdminDashboard.vue b/frontend/src/views/platform-admin/AdminDashboard.vue
index f61f141..661aeec 100644
--- a/frontend/src/views/platform-admin/AdminDashboard.vue
+++ b/frontend/src/views/platform-admin/AdminDashboard.vue
@@ -2,8 +2,10 @@
import { ref, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'
import { useRouter } from 'vue-router'
-import { Key, KeyRound, Users, DollarSign, Server, UserPlus, ArrowRight, ScrollText, CreditCard, MonitorCog } from 'lucide-vue-next'
import { safeCurrency, safeLocaleString } from '@/utils/formatters'
+import Panel from '@/components/ds/data/Panel.vue'
+import StatCard from '@/components/ds/data/StatCard.vue'
+import Icon from '@/components/ds/core/Icon.vue'
const api = useApi()
const router = useRouter()
@@ -21,26 +23,26 @@ const stats = ref(null)
const isLoading = ref(false)
const kpiCards = [
- { key: 'total_licenses' as const, label: 'Total Licenses', icon: Key, format: 'number' },
- { key: 'active_licenses' as const, label: 'Active Licenses', icon: KeyRound, format: 'number' },
- { key: 'total_users' as const, label: 'Total Users', icon: Users, format: 'number' },
- { key: 'module_mrr' as const, label: 'Module MRR', icon: DollarSign, format: 'currency' },
- { key: 'servers_online' as const, label: 'Servers Online', icon: Server, format: 'number' },
- { key: 'new_signups_this_week' as const, label: 'New Signups This Week', icon: UserPlus, format: 'number' },
+ { key: 'total_licenses' as const, label: 'Total licenses', icon: 'key', format: 'number' },
+ { key: 'active_licenses' as const, label: 'Active licenses', icon: 'key', format: 'number' },
+ { key: 'total_users' as const, label: 'Total users', icon: 'users', format: 'number' },
+ { key: 'module_mrr' as const, label: 'Module MRR', icon: 'dollar-sign', format: 'currency' },
+ { key: 'servers_online' as const, label: 'Servers online', icon: 'server', format: 'number' },
+ { key: 'new_signups_this_week' as const, label: 'New signups this week', icon: 'users', format: 'number' },
]
const quickLinks = [
- { label: 'Licenses', description: 'Manage license keys and activations', icon: Key, route: '/admin/licenses' },
- { label: 'Subscriptions', description: 'View module subscriptions and MRR', icon: CreditCard, route: '/admin/subscriptions' },
- { label: 'Users', description: 'Manage platform users and permissions', icon: Users, route: '/admin/users' },
- { label: 'Servers', description: 'Monitor connected game servers', icon: MonitorCog, route: '/admin/servers' },
+ { label: 'Licenses', description: 'Manage license keys and activations', icon: 'key', route: '/admin/licenses' },
+ { label: 'Subscriptions', description: 'View module subscriptions and MRR', icon: 'credit-card', route: '/admin/subscriptions' },
+ { label: 'Users', description: 'Manage platform users and permissions', icon: 'users', route: '/admin/users' },
+ { label: 'Servers', description: 'Monitor connected game servers', icon: 'server-cog', route: '/admin/servers' },
]
function formatValue(value: number | undefined, format: string): string {
if (format === 'currency') {
- return safeCurrency(value, '$', '\u2014')
+ return safeCurrency(value, '$', '—')
}
- return safeLocaleString(value, '\u2014')
+ return safeLocaleString(value, '—')
}
async function fetchStats() {
@@ -60,58 +62,91 @@ onMounted(() => {
-
-
-
-
-
-
Platform Admin
+
+
+
+
+
+
+
+
+
Platform admin
+
Dashboard
+
-
Overview of all platform activity and key metrics.
-
-
-
+
+
-
-
-
-
-
{{ card.label }}
-
-
-
-
-
- {{ stats ? formatValue(stats[card.key], card.format) : '\u2014' }}
-
-
+ :icon="card.icon"
+ :label="card.label"
+ :value="isLoading ? '—' : (stats ? formatValue(stats[card.key], card.format) : '—')"
+ />
-
-
-
Quick Links
-
+
+
+
-
-
-
-
-
+
+
-
{{ link.label }}
-
{{ link.description }}
+
+
{{ link.label }}
+
{{ link.description }}
+
+
-
+
+
+
diff --git a/frontend/src/views/platform-admin/AdminLicenses.vue b/frontend/src/views/platform-admin/AdminLicenses.vue
index 58724ab..995884d 100644
--- a/frontend/src/views/platform-admin/AdminLicenses.vue
+++ b/frontend/src/views/platform-admin/AdminLicenses.vue
@@ -1,7 +1,14 @@
-
-
-
-
-
+
+
+
+
+
+
+
-
License Management
-
{{ total }} licenses total
+
Platform admin
+
License management
-
-
-
- Export CSV
-
-
-
- Generate License
-
+
+ Export CSV
+ Generate license
-
-
-
-
Generate New License
-
-
-
-
-
+
-
-
-
+
+
-
- | License Key |
- Owner Email |
- Server Name |
- Status |
- Created |
- Expires |
+
+ | License key |
+ Owner email |
+ Server name |
+ Status |
+ Created |
+ Expires |
-
-
- |
- No licenses matching your filters.
- No licenses found.
- |
-
+
- | Loading licenses... |
+ Loading licenses... |
+
+
+ |
+
+ |
- | {{ license.license_key }} |
- {{ license.owner_email }} |
- {{ license.server_name }} |
-
-
- {{ license.status }}
-
+ | {{ license.license_key }} |
+ {{ license.owner_email }} |
+ {{ license.server_name ?? '—' }} |
+
+ {{ license.status }}
|
- {{ formatDate(license.created_at) }} |
- {{ formatDate(license.expires_at) }} |
+ {{ formatDate(license.created_at) }} |
+ {{ formatDate(license.expires_at) }} |
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
License Details
-
-
-
-
-
-
-
License Key
-
{{ selectedDetail.license_key }}
+
+
+
License key
+
{{ selectedDetail.license_key }}
-
-
Owner
-
{{ selectedDetail.owner_email }}
+
+
Owner
+
{{ selectedDetail.owner_email }}
-
-
Team Members
-
{{ selectedDetail.team_count }}
+
+
Team members
+
{{ selectedDetail.team_count }}
-
-
Wipe Count
-
{{ selectedDetail.wipe_count }}
+
+
Wipe count
+
{{ selectedDetail.wipe_count }}
-
-
Server Connection
-
-
-
Connection Type
-
{{ selectedDetail.server_connection.connection_type }}
+
+
Server connection
+
+
+
Connection type
+
{{ selectedDetail.server_connection.connection_type }}
-
-
Server IP
-
{{ selectedDetail.server_connection.server_ip }}
+
+
Server IP
+
{{ selectedDetail.server_connection.server_ip }}
-
-
Game Port
-
{{ selectedDetail.server_connection.game_port }}
+
+
Game port
+
{{ selectedDetail.server_connection.game_port }}
-
-
Status
-
{{ selectedDetail.server_connection.status }}
+
+
Status
+
{{ selectedDetail.server_connection.status }}
-
+
-
-
- Page {{ page }} of {{ totalPages }}
-
-
-
-
- Prev
-
-
- Next
-
-
+
+
+
diff --git a/frontend/src/views/platform-admin/AdminServers.vue b/frontend/src/views/platform-admin/AdminServers.vue
index d2445fa..259d92e 100644
--- a/frontend/src/views/platform-admin/AdminServers.vue
+++ b/frontend/src/views/platform-admin/AdminServers.vue
@@ -1,7 +1,13 @@
-
-
-
-
-
-
Server Overview
-
- {{ filteredServers.length }} server{{ filteredServers.length !== 1 ? 's' : '' }}
- ({{ statusFilter }})
-
+
+
+
+
+
+
+
+
+
Platform admin
+
Server overview
+
-
-
-
+
+
-
- | Server Name |
- Owner Email |
- Connection Type |
- Status |
- Server IP |
- Game Port |
- Last Heartbeat |
+
+ | Server name |
+ Owner email |
+ Connection type |
+ Status |
+ Server IP |
+ Game port |
+ Last heartbeat |
-
-
- |
- No servers matching "{{ searchQuery }}"
- No {{ statusFilter }} servers found.
- No servers found.
- |
-
+
- | Loading servers... |
+ Loading servers... |
+
+
+ |
+
+ |
- | {{ srv.server_name || 'Unnamed' }} |
- {{ srv.owner_email }} |
-
-
+ | {{ srv.server_name ?? 'Unnamed' }} |
+ {{ srv.owner_email }} |
+
+
{{ srv.connection_type.replace('_', ' ') }}
-
+
|
-
-
-
-
- {{ srv.connection_status }}
-
+
+
+
+ {{ srv.connection_status }}
|
- {{ srv.server_ip || '—' }} |
- {{ srv.game_port || '—' }} |
- {{ srv.plugin_last_seen ? relativeTime(srv.plugin_last_seen) : srv.companion_last_seen ? relativeTime(srv.companion_last_seen) : 'Never' }} |
+ {{ srv.server_ip ?? '—' }} |
+ {{ srv.game_port ?? '—' }} |
+
+ {{ srv.plugin_last_seen
+ ? relativeTime(srv.plugin_last_seen)
+ : srv.companion_last_seen
+ ? relativeTime(srv.companion_last_seen)
+ : 'Never' }}
+ |
|
-
+
+
+
diff --git a/frontend/src/views/platform-admin/AdminSubscriptions.vue b/frontend/src/views/platform-admin/AdminSubscriptions.vue
index 2e26732..0010030 100644
--- a/frontend/src/views/platform-admin/AdminSubscriptions.vue
+++ b/frontend/src/views/platform-admin/AdminSubscriptions.vue
@@ -1,8 +1,12 @@
-
-
-
-
-
-
Subscriptions
-
Module subscription overview and subscriber details.
+
+
+
+
+
+
+
+
+
Platform admin
+
Subscriptions
+
-
-
-
-
-
-
-
-
-
Total Subscribers
-
-
-
{{ safeLocaleString(totalSubscribers) }}
-
-
-
-
-
-
-
{{ safeCurrency(totalMrr, '$') }}
-
-
-
-
+
+
+
+
-
- {{ mod.count }}
- subscribers
-
+ icon="package"
+ :label="mod.name"
+ :value="String(mod.count)"
+ note="subscribers"
+ />
+
+
+
+
+
+
+
+
{{ mod.name }}
+
{{ mod.count }}
+
+
+
+
-
-
+
+
-
- | Owner Email |
- Module Name |
- License ID |
+
+ | Owner email |
+ Module name |
+ License ID |
-
-
- |
- No subscriptions found.
- |
-
+
- | Loading subscriptions... |
+ Loading subscriptions... |
+
+
+ |
+
+ |
- | {{ sub.owner_email }} |
- {{ sub.module_name }} |
- {{ sub.license_id }} |
+ {{ sub.owner_email }} |
+ {{ sub.module_name }} |
+ {{ sub.license_id }} |
-
+
+
+
diff --git a/frontend/src/views/platform-admin/AdminUsers.vue b/frontend/src/views/platform-admin/AdminUsers.vue
index 4e40975..96d9f0b 100644
--- a/frontend/src/views/platform-admin/AdminUsers.vue
+++ b/frontend/src/views/platform-admin/AdminUsers.vue
@@ -1,7 +1,13 @@
-
-
-
-
-
-
User Management
-
{{ total }} registered users
+
+
+
+
+
+
+
+
+
Platform admin
+
User management
+
-
-
-
-
-
+
+
-
-
+
+
-
- | Email |
- Username |
- Super Admin |
- Licenses |
- Created |
- Last Login |
- Actions |
+
+ | Email |
+ Username |
+ Super admin |
+ Licenses |
+ Created |
+ Last login |
+ Actions |
-
-
- |
- No users matching "{{ searchQuery }}"
- No users found.
- |
-
+
- | Loading users... |
+ Loading users... |
+
+
+ |
+
+ |
- | {{ user.email }} |
- {{ user.username }} |
-
-
-
- Super Admin
-
- —
+ | {{ user.email }} |
+ {{ user.username }} |
+
+ Super admin
+ —
|
- {{ user.license_count }} |
- {{ formatDate(user.created_at) }} |
- {{ formatLastLogin(user.last_login_at) }} |
-
-
- {{ user.license_count }} |
+ {{ formatDate(user.created_at) }} |
+ {{ formatLastLogin(user.last_login_at) }} |
+
+
+
+
-
-
-
-
-
-
+ />
|
-
+
-
-
- Page {{ page }} of {{ totalPages }}
-
-
-
-
- Prev
-
-
- Next
-
-
+
+
+
diff --git a/frontend/src/views/public/ServerInfoView.vue b/frontend/src/views/public/ServerInfoView.vue
index 5de6712..efb94c7 100644
--- a/frontend/src/views/public/ServerInfoView.vue
+++ b/frontend/src/views/public/ServerInfoView.vue
@@ -1,7 +1,14 @@
-
-
-
-
+
+
+
+
-
-
-
-
-
Server Not Found
-
{{ error }}
-
+
+
+
-
-
-
-
-
![]()
-
+
+
+
+
-
-
-
-
-
{{ serverInfo.server_name }}
-
-
-
{{ serverInfo.player_count }}/{{ serverInfo.max_players }} players online
+
+
+
+
![]()
+
+
+
+
+
+
+
{{ serverInfo.server_name }}
+
+ {{ serverInfo.description }}
+
+
+ Connect
+
-
-
- Connect
-
+
+
+
+
+
+
-
- {{ serverInfo.description }}
-
-
+
+
+ {{ serverInfo.motd }}
+
-
-
-
Message of the Day
-
{{ serverInfo.motd }}
-
-
-
-
-
-
-
Wipe Schedule
-
-
{{ serverInfo.wipe_schedule }}
-
-
-
-
-
Active Mods
-
-
- {{ mod }}
-
-
-
-
-
-
-
-
-
-
Join our Discord
+
+
+
+ {{ mod }}
-
-
- Join
-
-
-
-
+
+
+
+
+
+
+
+ Join Discord
+
+
+
+
+
+
+
+
diff --git a/frontend/src/views/public/StatusPageView.vue b/frontend/src/views/public/StatusPageView.vue
index cb93612..fbcee05 100644
--- a/frontend/src/views/public/StatusPageView.vue
+++ b/frontend/src/views/public/StatusPageView.vue
@@ -1,8 +1,16 @@
-
-
-
-
-
-
-
-
- Corrosion Status
-
-
- Real-time status for all Corrosion-powered Rust servers
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
Real-time status for all Corrosion-powered servers
-
-
-
-
-
-
- Total Servers
-
-
- {{ platformHealth.total_servers }}
-
-
-
-
-
-
- {{ platformHealth.online_servers }}
-
-
-
-
-
-
- Total Players
-
-
- {{ platformHealth.total_players }}
-
-
-
-
-
-
- Platform Uptime
-
-
- {{ safeFixed(platformHealth.uptime_percent, 1) }}%
-
-
+
+
-
-
-
-
-
-
Loading server status...
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading server status...
-
-
+
+
-
-
-
-
- {{ searchQuery ? 'No servers match your search' : 'No servers available yet' }}
-
-
+
+
-
-
-
+
+
-
-
-
-
- {{ server.server_name }}
-
-
- {{ server.subdomain }}.corrosionmgmt.com
-
+
+
+
-
-
-
-
- {{ getStatusText(server.status) }}
-
+
+
{{ server.description }}
+
+
+
+
+
+ {{ server.player_count }} / {{ server.max_players }} players
+
+
+
+
+
+
+
+ {{ server.map_name }}
+
+
+
+
+
Wipe schedule
+
{{ server.wipe_schedule }}
+
+ Next: {{ formatTimeUntil(server.next_wipe) }}
+
+
+
+
+
+
+ {{ safeFixed(server.uptime_24h_percent, 1) }}% 24h
+
+
+ {{ safeFixed(server.uptime_7d_percent, 1) }}% 7d
+
+
+ {{ safeFixed(server.uptime_30d_percent, 1) }}% 30d
+
-
-
-
- {{ server.description }}
-
-
-
-
-
-
- {{ server.player_count }} / {{ server.max_players }} players
-
-
-
-
-
-
- Map: {{ server.map_name }}
-
-
-
-
-
Wipe Schedule
-
{{ server.wipe_schedule }}
-
- Next wipe: {{ formatTimeUntil(server.next_wipe) }}
-
-
-
-
-
-
-
{{ safeFixed(server.uptime_24h_percent, 1) }}%
-
24h
-
-
-
{{ safeFixed(server.uptime_7d_percent, 1) }}%
-
7d
-
-
-
{{ safeFixed(server.uptime_30d_percent, 1) }}%
-
30d
-
-
-
+
-
-
- Auto-refreshing every 10 seconds
-
+
+
Auto-refreshing every 10 seconds
-
-
-
+
+
diff --git a/frontend/src/views/public/StoreView.vue b/frontend/src/views/public/StoreView.vue
index 8988a67..465b644 100644
--- a/frontend/src/views/public/StoreView.vue
+++ b/frontend/src/views/public/StoreView.vue
@@ -2,8 +2,16 @@
import { ref, computed, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import type { PublicStoreInfo, PublicStoreItem, StorePurchaseRequest, StorePurchaseResponse } from '@/types'
-import { ShoppingCart, Package, Filter, X, AlertCircle, ExternalLink, Check } from 'lucide-vue-next'
import { safeCurrency } from '@/utils/formatters'
+import Panel from '@/components/ds/data/Panel.vue'
+import Badge from '@/components/ds/core/Badge.vue'
+import Button from '@/components/ds/core/Button.vue'
+import Icon from '@/components/ds/core/Icon.vue'
+import Alert from '@/components/ds/feedback/Alert.vue'
+import EmptyState from '@/components/ds/feedback/EmptyState.vue'
+import Input from '@/components/ds/forms/Input.vue'
+import Select from '@/components/ds/forms/Select.vue'
+import Logo from '@/components/ds/brand/Logo.vue'
const route = useRoute()
const subdomain = computed(() => route.params.subdomain as string)
@@ -29,7 +37,7 @@ const categories = computed(() => {
})
return Array.from(cats).map(name => ({
value: name,
- label: name === 'all' ? 'All Items' : name
+ label: name === 'all' ? 'All items' : name
}))
})
@@ -142,14 +150,14 @@ function formatPrice(price: number): string {
return safeCurrency(price, '$')
}
-function itemTypeBadgeClass(itemType: string): string {
- const colors: Record = {
- kit: 'bg-blue-500/15 text-blue-400',
- rank: 'bg-purple-500/15 text-purple-400',
- currency: 'bg-green-500/15 text-green-400',
- custom_command: 'bg-orange-500/15 text-orange-400',
+function itemTypeTone(itemType: string): 'info' | 'wiping' | 'online' | 'warn' | 'neutral' {
+ const tones: Record = {
+ kit: 'info',
+ rank: 'wiping',
+ currency: 'online',
+ custom_command: 'warn',
}
- return colors[itemType] || 'bg-neutral-700/50 text-neutral-400'
+ return tones[itemType] ?? 'neutral'
}
onMounted(() => {
@@ -158,264 +166,631 @@ onMounted(() => {
-
-
-
-
-
-
-
- {{ storeInfo?.store_name || 'Server Store' }}
-
+
+
+
+
+
+
+
{{ storeInfo.description }}
-
-
-
-
+
+
+
+
+ Loading store...
-
-
-
-
Store Unavailable
-
{{ storeError }}
-
+
+
- Retry
-
+
+ Retry
+
+
-
+
-
-
-
-
+
-
+
![]()
-
-
+
+
-
-
- {{ item.category_name }}
-
+
+ {{ item.category_name }}
-
-
-
-
- {{ item.name }}
-
-
- {{ formatPrice(item.price) }}
-
-
-
- {{ item.description }}
-
+
+
+
{{ item.name }}
+ {{ formatPrice(item.price) }}
+
{{ item.description }}
-
-
-
+
+
+
{{ item.item_type.replace('_', ' ') }}
+
+
+ Limit {{ item.limit_per_player }} per player
-
-
- Limited to {{ item.limit_per_player }} per player
-
-
-
-
- Buy Now
-
+ >Buy now
-
-
-
-
No Items Available
-
- {{ selectedCategory === 'all' ? 'This store has no items at the moment.' : 'No items in this category.' }}
-
-
-
+
+
+
-
+
-
-
-
-
-
Complete Purchase
-
You'll be redirected to PayPal to complete payment
+
+
+
+
+
Complete purchase
+
You'll be redirected to PayPal to complete payment
-
+
-
-
+
+
-
-
+
+
![]()
-
-
-
{{ selectedItem.name }}
-
- {{ selectedItem.description }}
-
-
{{ formatPrice(selectedItem.price) }}
+
+
{{ selectedItem.name }}
+
{{ selectedItem.description }}
+
{{ formatPrice(selectedItem.price) }}
-
-
-
- Steam ID *
-
-
-
- Required for item delivery. Must be your 17-digit Steam ID.
-
-
+
+
-
-
-
- Player Name (Optional)
-
-
-
+
+
-
-
-
-
{{ purchaseError }}
-
+
+
-
-
-
-
- Items will be delivered automatically to your in-game character after payment
-
-
-
+
+
+
+
+ Items delivered automatically to your character after payment
+
+
+
All purchases are final and non-refundable
-
-
-
+
+
+
You must be logged into the server to receive items
-
-
-
+
+
+
-
-
-
+
-
-
-
+
+