Files
plugin-mivo-theme/theme/login.html
2026-01-19 14:13:57 +07:00

402 lines
31 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="expires" content="-1">
<link rel="icon" href="favicon.ico">
<link rel="stylesheet" href="assets/css/styles.css">
<script defer="" src="assets/js/alpine.min.js"></script>
<script src="assets/js/main.js"></script>
<script src="assets/js/theme.js"></script>
<script src="assets/js/i18n.js"></script>
<title>Mivo Hotspot - Login</title>
<script src="assets/js/md5.js"></script>
<script src="assets/js/html5-qrcode.min.js"></script>
</head>
<body class="min-h-screen flex flex-col items-center justify-start p-4 pt-36 bg-background transition-colors duration-500" x-data="initTheme()">
<div class="fixed inset-0 z-0 pointer-events-none">
<!-- Subtle Grid Pattern -->
<div class="absolute inset-0 bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiIGZpbGw9InJnYmEoMCwwLDAsMC4wNSkiLz48L3N2Zz4=')] dark:bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiIGZpbGw9InJnYmEoMjU1LDI1NSwyNTUsMC4wNSkiLz48L3N2Zz4=')] [mask-image:linear-gradient(to_bottom,white,transparent)]"></div>
<!-- glowing blobs -->
<div class="absolute -top-[20%] -left-[10%] w-[70vw] h-[70vw] rounded-full bg-blue-500/10 dark:bg-blue-500/5 blur-[120px] animate-pulse" style="animation-duration: 4s;"></div>
<div class="absolute top-[30%] -right-[15%] w-[60vw] h-[60vw] rounded-full bg-purple-500/10 dark:bg-purple-500/5 blur-[100px] animate-pulse" style="animation-duration: 6s; animation-delay: 1s;"></div>
</div>
<div class="nav-card transition-all duration-500 transform" x-data="{ showNav: true, lastScrollY: 0 }" @scroll.window="
showNav = window.scrollY < lastScrollY || window.scrollY < 50;
lastScrollY = window.scrollY;
" :class="showNav ? 'translate-y-0 opacity-100' : '-translate-y-24 opacity-0 pointer-events-none'">
<div class="theme-toggle-container">
<div class="theme-slider" :class="theme === 'dark' ? 'translate-x-[36px]' : 'translate-x-0'"></div>
<button @click="setTheme('light')" :class="theme === 'light' ? 'text-black' : 'text-slate-500 hover:text-slate-700'" class="theme-btn">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
</button>
<button @click="setTheme('dark')" :class="theme === 'dark' ? 'text-white' : 'text-slate-300 hover:text-white'" class="theme-btn">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
</button>
</div>
<div class="h-6 w-px bg-black/5 dark:bg-white/10 mx-1"></div>
<div class="relative" x-data="{ open: false, lang: localStorage.getItem('mivo_lang') || 'en' }" @language-changed.window="lang = $event.detail.lang">
<button @click="open = !open" class="flex items-center gap-2 px-3 h-10 rounded-full bg-black/5 dark:bg-white/5 border border-black/5 dark:border-white/10 text-xs font-bold text-foreground hover:bg-black/10 dark:hover:bg-white/20 transition-all backdrop-blur-md min-w-[80px] justify-between shadow-sm">
<div class="flex items-center gap-2">
<img :src="'assets/svg/' + (lang === 'id' ? 'id' : 'us') + '.svg'" class="w-4 h-3 rounded-sm object-cover" alt="Flag">
<span x-text="lang.toUpperCase()">EN</span>
</div>
<svg xmlns="http://www.w3.org/2000/svg" :class="open ? 'rotate-180' : ''" class="w-3.5 h-3.5 transition-transform text-accents-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg>
</button>
<div x-show="open" @click.away="open = false" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100" style="display: none;" class="absolute right-0 mt-2 w-48 rounded-xl bg-white/90 dark:bg-black/80 border border-black/5 dark:border-white/10 backdrop-blur-xl shadow-xl overflow-hidden py-1 ring-1 ring-black/5">
<button @click="changeLanguage('id'); open = false" class="w-full text-left px-4 py-3 text-xs font-medium text-foreground hover:bg-black/5 dark:hover:bg-white/10 transition-colors flex items-center justify-between">
<div class="flex items-center gap-3">
<img src="assets/svg/id.svg" class="w-4 h-3 rounded-sm object-cover" alt="ID">
<span>Bahasa Indonesia</span>
</div>
<span class="text-[10px] text-accents-5 font-bold">ID</span>
</button>
<button @click="changeLanguage('en'); open = false" class="w-full text-left px-4 py-3 text-xs font-medium text-foreground hover:bg-black/5 dark:hover:bg-white/10 transition-colors flex items-center justify-between">
<div class="flex items-center gap-3">
<img src="assets/svg/us.svg" class="w-4 h-3 rounded-sm object-cover" alt="US">
<span>English</span>
</div>
<span class="text-[10px] text-accents-5 font-bold">EN</span>
</button>
</div>
</div>
</div>
<script src="assets/js/qr.js"></script>
<script>
function loginData() {
return {
...qrMixin(),
loading: false,
checkLoading: false,
loginType: 'voucher', // 'voucher', 'member', 'check'
checkCode: '',
checkRes: null,
checkError: false,
showPass: false,
// API config
hasApi: window.MivoConfig?.apiBaseUrl !== '' || window.MivoConfig?.debugMode,
auth: {
username: '',
password: '',
voucher: ''
},
async checkVoucher() {
if (!this.checkCode) return;
this.checkLoading = true;
this.checkRes = null;
this.checkError = '';
try {
const response = await fetch(`${window.MivoConfig.apiBaseUrl}/api/status/check`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
session: window.MivoConfig.apiSession,
code: this.checkCode
})
});
const result = await response.json();
if (result.success) {
this.checkRes = result.data;
} else {
this.checkError = result.message || 'not_found';
}
} catch (e) {
console.error(e);
this.checkError = 'Connection failed';
} finally {
this.checkLoading = false;
}
},
get submitUsername() {
return this.loginType === 'voucher' ? this.auth.voucher : this.auth.username;
},
get submitPassword() {
return this.loginType === 'voucher' ? this.auth.voucher : this.auth.password;
},
submit() {
// Fix: Intercept 'check' mode to prevent login submission
if (this.loginType === 'check') {
this.checkVoucher();
return;
}
this.loading = true;
if (typeof hexMD5 === 'function' && document.sendin) {
document.sendin.username.value = this.submitUsername;
document.sendin.password.value = hexMD5('$(chap-id)' + this.submitPassword + '$(chap-challenge)');
document.sendin.submit();
} else {
// For PAP: form 'login' already has hidden inputs with x-model binding to submitUsername/Password
// We just need to ensure the Values are ready.
// Since we use :value on hidden inputs, they update reactively.
// However, we must wait for Alpine tick? Actually native submit picks up current DOM value.
// Just in case, force update? No need.
setTimeout(() => {
document.getElementById('send-form').submit();
}, 50);
}
}
}
}
</script>
<div class="relative w-full max-w-md" x-data="loginData()">
<div class="card overflow-hidden">
$(if chap-id)
<form name="sendin" action="$(link-login-only)" method="post" style="display:none">
<input type="hidden" name="username">
<input type="hidden" name="password">
<input type="hidden" name="dst" value="$(link-orig)">
<input type="hidden" name="popup" value="true">
</form>
$(endif)
<div class="text-center mb-6">
<div class="flex justify-center mb-4">
<img :src="'assets/img/' + (theme === 'light' ? 'logo-m' : 'logo-m-dark') + '.svg'" alt="Mivo Logo" class="h-10 w-auto">
</div>
<h1 class="text-xl font-bold tracking-tight text-foreground" data-i18n="hotspot.title">Mivo Hotspot</h1>
</div>
<!-- Tabs -->
<div class="tabs-container">
<div class="tab-slider" :class="{
'w-[calc(50%-4px)]': !hasApi,
'w-[calc(33.33%-4px)]': hasApi,
'translate-x-0': loginType === 'voucher',
'translate-x-[calc(100%+4px)]': loginType === 'member' && hasApi,
'translate-x-full': loginType === 'member' && !hasApi,
'translate-x-[calc(200%+8px)]': loginType === 'check' && hasApi
}"></div>
<button @click="loginType = 'voucher'" :class="loginType === 'voucher' ? 'tab-link-active' : 'tab-link-inactive'" class="tab-link" data-i18n="hotspot.voucher">Voucher</button>
<button @click="loginType = 'member'" :class="loginType === 'member' ? 'tab-link-active' : 'tab-link-inactive'" class="tab-link" data-i18n="hotspot.member">Member</button>
<template x-if="hasApi">
<button @click="loginType = 'check'" :class="loginType === 'check' ? 'tab-link-active' : 'tab-link-inactive'" class="tab-link" data-i18n="hotspot.check">Check</button>
</template>
</div>
<form id="send-form" name="login" action="$(link-login-only)" method="post" @submit.prevent="submit()">
<input type="hidden" name="dst" value="$(link-orig)">
<input type="hidden" name="popup" value="true">
<!-- Actual Submission Inputs -->
<input type="hidden" name="username" :value="submitUsername">
<input type="hidden" name="password" :value="submitPassword">
<!-- Sliding Tab Content -->
<div class="tab-window">
<div :class="{
'tab-track': !hasApi,
'tab-track-3': hasApi,
'translate-x-0': loginType === 'voucher',
'-translate-x-1/2': loginType === 'member' && !hasApi,
'-translate-x-1/3': loginType === 'member' && hasApi,
'-translate-x-2/3': loginType === 'check' && hasApi
}">
<!-- Voucher Pane -->
<div :class="hasApi ? 'tab-pane-3' : 'tab-pane'">
<div class="space-y-4 mb-4">
<div class="space-y-2">
<label class="text-xs font-semibold uppercase tracking-wider text-accents-5 ml-1" data-i18n="hotspot.voucher_code">Voucher Code</label>
<div class="flex gap-2">
<div class="relative flex-1">
<!-- Removed name="username" to prevent conflict -->
<input type="text" x-model="auth.voucher" placeholder="Voucher..." class="form-input text-lg tracking-widest font-mono focus:ring-foreground pr-10" data-i18n="hotspot.voucher_code" x-ref="voucherInput">
<button type="button" @click="initQr('voucher')" class="absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 hover:text-foreground">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><path d="M3 14h7v7H3z"></path></svg>
</button>
</div>
<button type="submit" :disabled="loading" class="btn-primary h-10 px-4 rounded-xl shrink-0 aspect-square flex items-center justify-center">
<svg x-show="!loading" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"></path></svg>
<svg x-show="loading" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
</button>
</div>
</div>
<!-- Trial Button -->
$(if trial == 'yes')
<a href="$(link-login-only)?dst=$(link-orig-esc)&amp;username=T-$(mac-esc)" class="w-full btn-secondary h-12 flex items-center justify-center gap-2 group">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-accents-5 group-hover:text-foreground transition-colors" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M2 12h20M5 5l14 14M19 5L5 19"></path></svg>
<span data-i18n="hotspot.trial">Free Trial Login</span>
</a>
$(endif)
</div>
</div>
<!-- Member Pane -->
<div :class="hasApi ? 'tab-pane-3' : 'tab-pane'">
<div class="space-y-4 mb-4">
<div class="space-y-2">
<label class="text-xs font-semibold uppercase tracking-wider text-accents-5 ml-1" data-i18n="hotspot.username">Username</label>
<div class="relative">
<!-- Removed name="username" -->
<input type="text" x-model="auth.username" placeholder="Username" class="form-input pr-10" data-i18n="hotspot.username">
<button type="button" @click="initQr('member')" class="absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 hover:text-foreground">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><path d="M3 14h7v7H3z"></path></svg>
</button>
</div>
</div>
<div class="space-y-2">
<label class="text-xs font-semibold uppercase tracking-wider text-accents-5 ml-1" data-i18n="hotspot.password">Password</label>
<div class="flex gap-2">
<div class="relative flex-1">
<!-- Removed name="password" -->
<input :type="showPass ? 'text' : 'password'" x-model="auth.password" placeholder="••••••••" class="form-input text-center pr-10" data-i18n="hotspot.password">
<button type="button" @click="showPass = !showPass" class="absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 hover:text-foreground">
<svg x-show="!showPass" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
<svg x-show="showPass" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1="1" y1="1" x2="23" y2="23"></line></svg>
</button>
</div>
<button type="submit" :disabled="loading" class="btn-primary h-10 px-4 rounded-xl shrink-0 aspect-square flex items-center justify-center">
<svg x-show="!loading" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"></path></svg>
<svg x-show="loading" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
</button>
</div>
</div>
</div>
</div>
<!-- Check Pane -->
<template x-if="hasApi">
<div class="tab-pane-3">
<div class="space-y-4 mb-4">
<div class="space-y-2">
<label class="text-xs font-semibold uppercase tracking-wider text-accents-5 ml-1" data-i18n="check.title">Check Voucher</label>
<div class="flex gap-2">
<div class="relative flex-1">
<input type="text" x-model="checkCode" placeholder="Voucher..." class="form-input text-lg tracking-widest font-mono focus:ring-foreground pr-10">
<button type="button" @click="initQr('check')" class="absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 hover:text-foreground">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><path d="M3 14h7v7H3z"></path></svg>
</button>
</div>
<button type="button" @click="checkVoucher()" :disabled="checkLoading" class="btn-primary h-10 px-4 rounded-xl shrink-0 aspect-square flex items-center justify-center">
<svg x-show="!checkLoading" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
<svg x-show="checkLoading" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
</button>
</div>
</div>
<!-- Result Area -->
<div x-show="checkRes || checkError" x-transition="" class="card-inner p-3 min-h-[100px] flex flex-col justify-center">
<div x-show="checkRes" class="space-y-2">
<div class="flex justify-between text-xs">
<span class="text-slate-500" data-i18n="check.status">Status</span>
<span class="font-bold text-emerald-500" x-text="checkRes?.status_label"></span>
</div>
<div class="flex justify-between text-xs">
<span class="text-slate-500" data-i18n="check.quota_left">Remaining</span>
<span class="font-bold text-foreground" x-text="checkRes?.data_left"></span>
</div>
<div class="flex justify-between text-xs">
<span class="text-slate-500" data-i18n="check.time_left">Time Left</span>
<span class="font-bold text-foreground" x-text="window.formatTime(checkRes?.time_left)"></span>
</div>
<div class="flex justify-between text-xs">
<span class="text-slate-500" data-i18n="check.expiration">Expires</span>
<span class="font-bold text-foreground" x-text="checkRes?.expiration"></span>
</div>
</div>
<div x-show="checkError" class="text-center text-red-500 text-xs py-4" data-i18n="check.not_found">
Voucher not found or not active.
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<!-- Error Message -->
$(if error)
<div x-data="{ errorShow: false }" x-init="$nextTick(() => errorShow = true)" style="display: none;" x-show="errorShow" x-transition:enter="transition ease-out duration-1000" x-transition:enter-start="opacity-0 translate-y-2 scale-95" x-transition:enter-end="opacity-100 translate-y-0 scale-100">
<div class="mt-4 p-3 rounded-xl bg-red-500/10 border border-red-500/20 text-red-500 text-xs flex items-start gap-3 shadow-lg shadow-red-500/5">
<div class="p-1 rounded-full bg-red-500/20 shrink-0">
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</div>
<span class="py-0.5 font-medium leading-relaxed">$(error)</span>
</div>
</div>
$(endif)
</form>
<div class="mt-8 pt-8 border-t border-accents-2 dark:border-white/10">
<p class="text-accents-5 text-[10px] tracking-widest uppercase">MIVO THEME BY DYZULKDEV • POWERED BY MIVO</p>
</div>
</div>
<!-- QR Modal -->
<!-- QR Modal -->
<div x-show="showQr" style="display: none;" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/80 backdrop-blur-sm p-4" x-transition.opacity="">
<div class="card w-full max-w-sm relative" @click.outside="closeQr()">
<button @click="closeQr()" class="absolute top-4 right-4 text-slate-400 hover:text-foreground">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
<h3 class="text-lg font-bold mb-4" x-text="qrType === 'login' ? 'Scan Login QR' : 'Scan Check QR'"></h3>
<div x-show="!qrResult" class="relative rounded-xl overflow-hidden bg-black/5 dark:bg-white/5 mb-4 aspect-square">
<div id="reader" class="w-full h-full object-cover"></div>
<!-- Overlay Guide -->
<div class="absolute inset-0 border-2 border-white/30 m-8 rounded-lg pointer-events-none">
<div class="absolute top-0 left-0 w-4 h-4 border-t-4 border-l-4 border-emerald-500 -mt-0.5 -ml-0.5"></div>
<div class="absolute top-0 right-0 w-4 h-4 border-t-4 border-r-4 border-emerald-500 -mt-0.5 -mr-0.5"></div>
<div class="absolute bottom-0 left-0 w-4 h-4 border-b-4 border-l-4 border-emerald-500 -mb-0.5 -ml-0.5"></div>
<div class="absolute bottom-0 right-0 w-4 h-4 border-b-4 border-r-4 border-emerald-500 -mb-0.5 -mr-0.5"></div>
</div>
</div>
<div x-show="!qrResult" class="grid grid-cols-2 gap-2 mb-4">
<button @click="switchCamera()" class="btn-secondary h-10 text-xs gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 7h-9"></path><path d="M14 17H5"></path><circle cx="17" cy="17" r="3"></circle><circle cx="7" cy="7" r="3"></circle></svg>
Flip Camera
</button>
<button @click="$refs.qrFile.click()" class="btn-secondary h-10 text-xs gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>
Check Image
</button>
<input type="file" x-ref="qrFile" class="hidden" accept="image/*" @change="scanFile($event)">
</div>
<!-- Validation Error -->
<div x-show="qrError" class="mb-4 p-3 rounded-lg bg-red-500/10 border border-red-500/20 text-red-500 text-sm text-center">
<span x-text="qrError"></span>
</div>
<!-- Confirmation Step -->
<div x-show="qrResult" class="text-center space-y-4">
<div class="w-16 h-16 mx-auto bg-green-500/20 rounded-full flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-8 h-8 text-green-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>
</div>
<div>
<h4 class="font-bold text-foreground">Scanned Successfully!</h4>
<p class="text-sm text-slate-400 mt-1" x-text="qrResult?.display"></p>
</div>
<div class="flex gap-2 mt-4">
<button @click="initQr(scanTarget)" class="w-full btn bg-slate-100 dark:bg-white/10 text-foreground hover:bg-slate-200 dark:hover:bg-white/20">Rescan</button>
<button @click="confirmQr()" class="w-full btn-primary">Confirm</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>