Files
videoPlayer/components/SettingsModal.vue
2025-10-02 01:52:03 -06:00

474 lines
10 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="isOpen" class="settings-overlay" @click.self="close">
<div class="settings-modal">
<div class="settings-header">
<h2> Configuración</h2>
<button @click="close" class="close-btn"></button>
</div>
<div class="settings-content">
<div v-if="!isAuthenticated" class="auth-section">
<p class="auth-label">Ingresa la contraseña para acceder a la configuración</p>
<input
v-model="password"
type="password"
placeholder="Contraseña"
class="password-input"
@keyup.enter="authenticate"
/>
<p v-if="authError" class="error-message">{{ authError }}</p>
<button @click="authenticate" class="auth-btn">Ingresar</button>
</div>
<div v-else class="config-section">
<div class="config-item">
<div class="config-info">
<h3>🚀 Inicio Automático</h3>
<p>El servidor se iniciará automáticamente al encender Windows</p>
</div>
<label class="toggle">
<input
type="checkbox"
v-model="autostartEnabled"
@change="toggleAutostart"
:disabled="loading"
/>
<span class="toggle-slider"></span>
</label>
</div>
<div class="instructions">
<h4>📝 Instrucciones de instalación:</h4>
<ol>
<li>Ejecuta <code>install-autostart.bat</code> desde la carpeta del proyecto</li>
<li>Esto copiará el script de autostart al Startup de Windows</li>
<li>Usa este toggle para habilitar/deshabilitar el autostart</li>
<li>Para desinstalar completamente, ejecuta <code>uninstall-autostart.bat</code></li>
</ol>
<h4 style="margin-top: 20px;">🎮 Scripts disponibles:</h4>
<ul>
<li><code>quantum-start.bat</code> - Iniciar el servidor</li>
<li><code>quantum-stop.bat</code> - Detener el servidor</li>
<li><code>install-autostart.bat</code> - Instalar autostart</li>
<li><code>uninstall-autostart.bat</code> - Desinstalar autostart</li>
</ul>
</div>
<p v-if="statusMessage" class="status-message" :class="{ error: isError }">
{{ statusMessage }}
</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps<{
isOpen: boolean
}>()
const emit = defineEmits<{
close: []
}>()
const password = ref('')
const isAuthenticated = ref(false)
const authError = ref('')
const autostartEnabled = ref(true)
const loading = ref(false)
const statusMessage = ref('')
const isError = ref(false)
const authenticate = async () => {
try {
const response = await $fetch('/api/config', {
method: 'POST',
body: {
password: password.value,
action: 'get-status'
}
})
isAuthenticated.value = true
authError.value = ''
autostartEnabled.value = response.autostart
} catch (error: any) {
authError.value = error.data?.message || 'Contraseña incorrecta'
}
}
const toggleAutostart = async () => {
loading.value = true
statusMessage.value = ''
isError.value = false
try {
const response = await $fetch('/api/config', {
method: 'POST',
body: {
password: password.value,
action: 'toggle-autostart'
}
})
autostartEnabled.value = response.autostart
statusMessage.value = response.autostart
? '✅ Autostart habilitado'
: '❌ Autostart deshabilitado'
setTimeout(() => {
statusMessage.value = ''
}, 3000)
} catch (error: any) {
isError.value = true
statusMessage.value = error.data?.message || 'Error al actualizar configuración'
// Revertir el toggle
autostartEnabled.value = !autostartEnabled.value
} finally {
loading.value = false
}
}
const close = () => {
emit('close')
// Reset after animation
setTimeout(() => {
isAuthenticated.value = false
password.value = ''
authError.value = ''
statusMessage.value = ''
}, 300)
}
watch(() => props.isOpen, (newVal) => {
if (newVal && isAuthenticated.value) {
// Refresh status cuando se abre
authenticate()
}
})
</script>
<style scoped>
.settings-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.settings-modal {
background: linear-gradient(135deg, rgba(10, 10, 15, 0.95), rgba(20, 20, 30, 0.95));
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 20px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 0 60px rgba(0, 255, 255, 0.3);
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25px;
border-bottom: 1px solid rgba(0, 255, 255, 0.2);
background: linear-gradient(135deg, rgba(0, 255, 255, 0.1), rgba(255, 0, 255, 0.1));
}
.settings-header h2 {
margin: 0;
font-size: 24px;
background: linear-gradient(135deg, #00ffff, #ff00ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.close-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
width: 35px;
height: 35px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.close-btn:hover {
background: rgba(255, 0, 0, 0.3);
border-color: rgba(255, 0, 0, 0.5);
transform: rotate(90deg);
}
.settings-content {
padding: 25px;
overflow-y: auto;
max-height: calc(80vh - 100px);
}
.auth-section {
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
padding: 40px 20px;
}
.auth-label {
color: #fff;
text-align: center;
font-size: 14px;
margin-bottom: 10px;
}
.password-input {
width: 100%;
max-width: 300px;
padding: 12px 20px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 10px;
color: #fff;
font-size: 16px;
text-align: center;
transition: all 0.3s ease;
}
.password-input:focus {
outline: none;
border-color: rgba(0, 255, 255, 0.6);
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
}
.auth-btn {
padding: 12px 40px;
background: linear-gradient(135deg, rgba(0, 255, 255, 0.2), rgba(255, 0, 255, 0.2));
border: 1px solid rgba(0, 255, 255, 0.5);
border-radius: 10px;
color: #fff;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.auth-btn:hover {
background: linear-gradient(135deg, rgba(0, 255, 255, 0.3), rgba(255, 0, 255, 0.3));
box-shadow: 0 0 30px rgba(0, 255, 255, 0.4);
transform: translateY(-2px);
}
.error-message {
color: #ff4444;
font-size: 14px;
text-align: center;
margin: 0;
}
.config-section {
display: flex;
flex-direction: column;
gap: 25px;
}
.config-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(0, 255, 255, 0.2);
border-radius: 12px;
transition: all 0.3s ease;
}
.config-item:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(0, 255, 255, 0.4);
}
.config-info h3 {
margin: 0 0 5px 0;
font-size: 18px;
color: #fff;
}
.config-info p {
margin: 0;
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
}
.toggle {
position: relative;
display: inline-block;
width: 60px;
height: 30px;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.2);
transition: 0.4s;
border-radius: 30px;
border: 1px solid rgba(255, 255, 255, 0.3);
}
.toggle-slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 4px;
bottom: 3px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}
input:checked + .toggle-slider {
background: linear-gradient(135deg, #00ffff, #00ff88);
border-color: #00ffff;
}
input:checked + .toggle-slider:before {
transform: translateX(30px);
}
input:disabled + .toggle-slider {
opacity: 0.5;
cursor: not-allowed;
}
.instructions {
padding: 20px;
background: rgba(0, 255, 255, 0.05);
border: 1px solid rgba(0, 255, 255, 0.2);
border-radius: 12px;
color: #fff;
}
.instructions h4 {
margin: 0 0 10px 0;
font-size: 16px;
color: #00ffff;
}
.instructions ol,
.instructions ul {
margin: 10px 0;
padding-left: 20px;
}
.instructions li {
margin: 8px 0;
font-size: 14px;
line-height: 1.6;
color: rgba(255, 255, 255, 0.9);
}
.instructions code {
background: rgba(0, 0, 0, 0.4);
padding: 2px 8px;
border-radius: 4px;
font-family: monospace;
color: #00ffff;
font-size: 13px;
}
.status-message {
padding: 12px;
background: rgba(0, 255, 0, 0.1);
border: 1px solid rgba(0, 255, 0, 0.3);
border-radius: 8px;
color: #00ff88;
text-align: center;
font-size: 14px;
margin: 0;
}
.status-message.error {
background: rgba(255, 0, 0, 0.1);
border-color: rgba(255, 0, 0, 0.3);
color: #ff4444;
}
@media (max-width: 768px) {
.settings-modal {
width: 95%;
max-height: 90vh;
}
.settings-header {
padding: 20px;
}
.settings-header h2 {
font-size: 20px;
}
.settings-content {
padding: 20px;
}
.config-item {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.instructions {
padding: 15px;
}
.instructions code {
font-size: 11px;
}
}
</style>