This commit is contained in:
rafaeldpsilva
2025-12-20 00:17:21 +00:00
parent 4b4338fb91
commit c3364cc422
31 changed files with 818 additions and 425 deletions

View File

@@ -28,9 +28,11 @@
<button
@click="activeSection = section.id"
class="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left transition-colors"
:class="activeSection === section.id
? 'bg-blue-100 text-blue-700'
: 'text-gray-700 hover:bg-gray-100'"
:class="
activeSection === section.id
? 'bg-blue-100 text-blue-700'
: 'text-gray-700 hover:bg-gray-100'
"
>
<span class="text-lg">{{ section.icon }}</span>
<div>
@@ -46,7 +48,10 @@
<!-- Settings Content -->
<div class="lg:col-span-2 space-y-6">
<!-- Appearance Settings -->
<div v-if="activeSection === 'appearance'" class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div
v-if="activeSection === 'appearance'"
class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden"
>
<div class="p-6 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-900">Appearance</h3>
<p class="text-gray-600 text-sm mt-1">Customize the look and feel of your dashboard</p>
@@ -61,9 +66,11 @@
:key="theme.value"
@click="settingsStore.updateSetting('theme', theme.value)"
class="relative p-4 border-2 rounded-lg cursor-pointer transition-all hover:border-blue-300"
:class="settingsStore.settings.theme === theme.value
? 'border-blue-500 bg-blue-50'
: 'border-gray-200'"
:class="
settingsStore.settings.theme === theme.value
? 'border-blue-500 bg-blue-50'
: 'border-gray-200'
"
>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
@@ -72,7 +79,11 @@
</div>
<div v-if="settingsStore.settings.theme === theme.value" class="text-blue-600">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
@@ -89,9 +100,11 @@
:key="mode.value"
@click="settingsStore.updateSetting('ui.navigationMode', mode.value)"
class="relative p-4 border-2 rounded-lg cursor-pointer transition-all hover:border-blue-300"
:class="settingsStore.settings.ui.navigationMode === mode.value
? 'border-blue-500 bg-blue-50'
: 'border-gray-200'"
:class="
settingsStore.settings.ui.navigationMode === mode.value
? 'border-blue-500 bg-blue-50'
: 'border-gray-200'
"
>
<div class="flex items-start justify-between">
<div class="flex items-start gap-3">
@@ -101,9 +114,16 @@
<div class="text-sm text-gray-600 mt-1">{{ mode.description }}</div>
</div>
</div>
<div v-if="settingsStore.settings.ui.navigationMode === mode.value" class="text-blue-600 mt-1">
<div
v-if="settingsStore.settings.ui.navigationMode === mode.value"
class="text-blue-600 mt-1"
>
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
@@ -119,12 +139,21 @@
<div class="text-sm text-gray-600">Reduce spacing and padding</div>
</div>
<button
@click="settingsStore.updateSetting('ui.compactMode', !settingsStore.settings.ui.compactMode)"
@click="
settingsStore.updateSetting(
'ui.compactMode',
!settingsStore.settings.ui.compactMode,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.ui.compactMode ? 'bg-blue-600' : 'bg-gray-200'"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.ui.compactMode ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.ui.compactMode ? 'translate-x-6' : 'translate-x-1'
"
></span>
</button>
</div>
@@ -134,12 +163,21 @@
<div class="text-sm text-gray-600">Enable smooth transitions</div>
</div>
<button
@click="settingsStore.updateSetting('ui.showAnimations', !settingsStore.settings.ui.showAnimations)"
@click="
settingsStore.updateSetting(
'ui.showAnimations',
!settingsStore.settings.ui.showAnimations,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.ui.showAnimations ? 'bg-blue-600' : 'bg-gray-200'"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.ui.showAnimations ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.ui.showAnimations ? 'translate-x-6' : 'translate-x-1'
"
></span>
</button>
</div>
</div>
@@ -147,7 +185,10 @@
</div>
<!-- Data & Sync Settings -->
<div v-if="activeSection === 'data'" class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div
v-if="activeSection === 'data'"
class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden"
>
<div class="p-6 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-900">Data & Synchronization</h3>
<p class="text-gray-600 text-sm mt-1">Configure data refresh and connection settings</p>
@@ -160,12 +201,19 @@
<div class="text-sm text-gray-600">Automatically refresh data periodically</div>
</div>
<button
@click="settingsStore.updateSetting('ui.autoRefresh', !settingsStore.settings.ui.autoRefresh)"
@click="
settingsStore.updateSetting(
'ui.autoRefresh',
!settingsStore.settings.ui.autoRefresh,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.ui.autoRefresh ? 'bg-blue-600' : 'bg-gray-200'"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.ui.autoRefresh ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.ui.autoRefresh ? 'translate-x-6' : 'translate-x-1'"
></span>
</button>
</div>
@@ -178,10 +226,17 @@
min="1"
max="60"
:value="settingsStore.settings.ui.refreshInterval"
@input="settingsStore.updateSetting('ui.refreshInterval', parseInt(($event.target as HTMLInputElement).value))"
@input="
settingsStore.updateSetting(
'ui.refreshInterval',
parseInt(($event.target as HTMLInputElement).value),
)
"
class="flex-1 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
/>
<div class="bg-gray-100 px-3 py-1 rounded text-sm font-medium min-w-[80px] text-center">
<div
class="bg-gray-100 px-3 py-1 rounded text-sm font-medium min-w-[80px] text-center"
>
{{ settingsStore.settings.ui.refreshInterval }}s
</div>
</div>
@@ -196,43 +251,62 @@
type="text"
placeholder="ws://localhost:8000/ws"
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
:class="{ 'border-red-300 focus:ring-red-500 focus:border-red-500': websocketUrlError }"
:class="{
'border-red-300 focus:ring-red-500 focus:border-red-500': websocketUrlError,
}"
/>
<button
@click="updateWebSocketUrl"
:disabled="!websocketUrlInput || websocketUrlInput === settingsStore.settings.websocketUrl"
:disabled="
!websocketUrlInput || websocketUrlInput === settingsStore.settings.websocketUrl
"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 text-white rounded-lg text-sm font-medium transition-colors"
>
Update
</button>
</div>
<p v-if="websocketUrlError" class="text-red-600 text-sm mt-1">{{ websocketUrlError }}</p>
<p v-else class="text-gray-500 text-sm mt-1">Current: {{ settingsStore.settings.websocketUrl }}</p>
<p v-if="websocketUrlError" class="text-red-600 text-sm mt-1">
{{ websocketUrlError }}
</p>
<p v-else class="text-gray-500 text-sm mt-1">
Current: {{ settingsStore.settings.websocketUrl }}
</p>
</div>
<!-- Auto Connect -->
<div class="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
<div>
<div class="font-medium text-gray-900">Auto Connect</div>
<div class="text-sm text-gray-600">Automatically connect to WebSocket on app start</div>
<div class="text-sm text-gray-600">
Automatically connect to WebSocket on app start
</div>
</div>
<button
@click="settingsStore.updateSetting('autoConnect', !settingsStore.settings.autoConnect)"
@click="
settingsStore.updateSetting('autoConnect', !settingsStore.settings.autoConnect)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.autoConnect ? 'bg-blue-600' : 'bg-gray-200'"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.autoConnect ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.autoConnect ? 'translate-x-6' : 'translate-x-1'"
></span>
</button>
</div>
</div>
</div>
<!-- Notifications Settings -->
<div v-if="activeSection === 'notifications'" class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div
v-if="activeSection === 'notifications'"
class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden"
>
<div class="p-6 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-900">Notifications</h3>
<p class="text-gray-600 text-sm mt-1">Configure how you receive alerts and notifications</p>
<p class="text-gray-600 text-sm mt-1">
Configure how you receive alerts and notifications
</p>
</div>
<div class="p-6 space-y-6">
<!-- Enable Notifications -->
@@ -242,34 +316,62 @@
<div class="text-sm text-gray-600">Receive system alerts and updates</div>
</div>
<button
@click="settingsStore.updateSetting('notifications.enabled', !settingsStore.settings.notifications.enabled)"
@click="
settingsStore.updateSetting(
'notifications.enabled',
!settingsStore.settings.notifications.enabled,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.notifications.enabled ? 'bg-blue-600' : 'bg-gray-200'"
:class="
settingsStore.settings.notifications.enabled ? 'bg-blue-600' : 'bg-gray-200'
"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.notifications.enabled ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.notifications.enabled ? 'translate-x-6' : 'translate-x-1'
"
></span>
</button>
</div>
<div v-if="settingsStore.settings.notifications.enabled" class="space-y-4">
<!-- Notification Types -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
<div
class="flex items-center justify-between p-4 border border-gray-200 rounded-lg"
>
<div>
<div class="font-medium text-gray-900">Sound Alerts</div>
<div class="text-sm text-gray-600">Play sound for notifications</div>
</div>
<button
@click="settingsStore.updateSetting('notifications.sound', !settingsStore.settings.notifications.sound)"
@click="
settingsStore.updateSetting(
'notifications.sound',
!settingsStore.settings.notifications.sound,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.notifications.sound ? 'bg-blue-600' : 'bg-gray-200'"
:class="
settingsStore.settings.notifications.sound ? 'bg-blue-600' : 'bg-gray-200'
"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.notifications.sound ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.notifications.sound
? 'translate-x-6'
: 'translate-x-1'
"
></span>
</button>
</div>
<div class="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
<div
class="flex items-center justify-between p-4 border border-gray-200 rounded-lg"
>
<div>
<div class="font-medium text-gray-900">Desktop Notifications</div>
<div class="text-sm text-gray-600">Show browser notifications</div>
@@ -277,10 +379,18 @@
<button
@click="toggleDesktopNotifications"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.notifications.desktop ? 'bg-blue-600' : 'bg-gray-200'"
:class="
settingsStore.settings.notifications.desktop ? 'bg-blue-600' : 'bg-gray-200'
"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.notifications.desktop ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.notifications.desktop
? 'translate-x-6'
: 'translate-x-1'
"
></span>
</button>
</div>
</div>
@@ -292,12 +402,27 @@
<div class="text-sm text-gray-600">Only show high-priority notifications</div>
</div>
<button
@click="settingsStore.updateSetting('notifications.criticalOnly', !settingsStore.settings.notifications.criticalOnly)"
@click="
settingsStore.updateSetting(
'notifications.criticalOnly',
!settingsStore.settings.notifications.criticalOnly,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.notifications.criticalOnly ? 'bg-blue-600' : 'bg-gray-200'"
:class="
settingsStore.settings.notifications.criticalOnly
? 'bg-blue-600'
: 'bg-gray-200'
"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.notifications.criticalOnly ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="
settingsStore.settings.notifications.criticalOnly
? 'translate-x-6'
: 'translate-x-1'
"
></span>
</button>
</div>
</div>
@@ -305,7 +430,10 @@
</div>
<!-- Advanced Settings -->
<div v-if="activeSection === 'advanced'" class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div
v-if="activeSection === 'advanced'"
class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden"
>
<div class="p-6 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-900">Advanced</h3>
<p class="text-gray-600 text-sm mt-1">Developer options and advanced configuration</p>
@@ -318,12 +446,19 @@
<div class="text-sm text-gray-600">Enable debug logs and development features</div>
</div>
<button
@click="settingsStore.updateSetting('developerMode', !settingsStore.settings.developerMode)"
@click="
settingsStore.updateSetting(
'developerMode',
!settingsStore.settings.developerMode,
)
"
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
:class="settingsStore.settings.developerMode ? 'bg-blue-600' : 'bg-gray-200'"
>
<span class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.developerMode ? 'translate-x-6' : 'translate-x-1'"></span>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
:class="settingsStore.settings.developerMode ? 'translate-x-6' : 'translate-x-1'"
></span>
</button>
</div>
@@ -357,7 +492,9 @@
</button>
</div>
<p v-if="importError" class="text-red-600 text-sm mt-1">{{ importError }}</p>
<p v-if="importSuccess" class="text-green-600 text-sm mt-1">Settings imported successfully!</p>
<p v-if="importSuccess" class="text-green-600 text-sm mt-1">
Settings imported successfully!
</p>
</div>
</div>
</div>
@@ -366,20 +503,24 @@
</div>
<!-- Reset Confirmation Dialog -->
<div v-if="showResetDialog" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
<div
v-if="showResetDialog"
class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"
>
<div class="bg-white rounded-xl max-w-md w-full p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Reset All Settings</h3>
<p class="text-gray-600 mb-4">
Are you sure you want to reset all settings to their default values? This action cannot be undone.
Are you sure you want to reset all settings to their default values? This action cannot be
undone.
</p>
<div class="flex gap-3">
<button
<button
@click="showResetDialog = false"
class="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-50 transition-colors"
>
Cancel
</button>
<button
<button
@click="resetAllSettings"
class="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-sm font-medium transition-colors"
>
@@ -412,26 +553,26 @@ const settingSections = [
id: 'appearance',
name: 'Appearance',
description: 'Theme & UI',
icon: '🎨'
icon: '🎨',
},
{
id: 'data',
name: 'Data & Sync',
description: 'Connection & refresh',
icon: '🔄'
icon: '🔄',
},
{
id: 'notifications',
name: 'Notifications',
description: 'Alerts & sounds',
icon: '🔔'
icon: '🔔',
},
{
id: 'advanced',
name: 'Advanced',
description: 'Developer options',
icon: '⚙️'
}
icon: '⚙️',
},
]
// Methods
@@ -439,7 +580,7 @@ const formatTime = (date: Date) => {
return new Intl.DateTimeFormat('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
second: '2-digit',
}).format(date)
}
@@ -448,7 +589,7 @@ const updateWebSocketUrl = () => {
websocketUrlError.value = 'Please enter a valid WebSocket URL (ws:// or wss://)'
return
}
settingsStore.updateSetting('websocketUrl', websocketUrlInput.value)
websocketUrlError.value = ''
}
@@ -470,7 +611,7 @@ const exportSettings = () => {
const settingsJson = settingsStore.exportSettings()
const blob = new Blob([settingsJson], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = `dashboard-settings-${new Date().toISOString().split('T')[0]}.json`
@@ -483,9 +624,9 @@ const exportSettings = () => {
const importSettings = () => {
importError.value = ''
importSuccess.value = false
const success = settingsStore.importSettings(importSettingsJson.value)
if (success) {
importSuccess.value = true
importSettingsJson.value = ''
@@ -508,4 +649,4 @@ onMounted(() => {
settingsStore.initialize()
websocketUrlInput.value = settingsStore.settings.websocketUrl
})
</script>
</script>