Spaces:
Sleeping
Sleeping
| --- | |
| import AdminLayout from '../../layouts/AdminLayout.astro'; | |
| import { txasupabase } from '../../lib/txasupabase.js'; | |
| const settings = await txasupabase.getSettings(); | |
| --- | |
| <AdminLayout title="Cấu hình hệ thống"> | |
| <div class="mb-12"> | |
| <h1 class="text-4xl font-black tracking-tighter text-white">Cấu hình Hệ thống</h1> | |
| <p class="mt-2 text-gray-500">Thiết lập toàn bộ tham số hoạt động của website, máy chủ SMTP email và cơ chế xác thực người dùng.</p> | |
| </div> | |
| <div class="glass rounded-[40px] p-8 md:p-10 border border-white/5 space-y-8"> | |
| <!-- Tab Header --> | |
| <div class="flex flex-wrap gap-3 border-b border-white/5 pb-6"> | |
| <button id="btn-tab-general" class="tab-btn active-tab flex items-center gap-2 rounded-2xl px-6 py-3.5 text-xs font-black uppercase tracking-widest transition-all cursor-pointer"> | |
| <i class="fas fa-desktop"></i> Cấu hình chung | |
| </button> | |
| <button id="btn-tab-smtp" class="tab-btn flex items-center gap-2 rounded-2xl px-6 py-3.5 text-xs font-black uppercase tracking-widest transition-all cursor-pointer"> | |
| <i class="fas fa-envelope"></i> SMTP Email | |
| </button> | |
| <button id="btn-tab-user" class="tab-btn flex items-center gap-2 rounded-2xl px-6 py-3.5 text-xs font-black uppercase tracking-widest transition-all cursor-pointer"> | |
| <i class="fas fa-user-shield"></i> Người dùng | |
| </button> | |
| <button id="btn-tab-social" class="tab-btn flex items-center gap-2 rounded-2xl px-6 py-3.5 text-xs font-black uppercase tracking-widest transition-all cursor-pointer"> | |
| <i class="fas fa-share-alt"></i> Mạng xã hội | |
| </button> | |
| </div> | |
| <!-- Tab Contents Form --> | |
| <form id="settings-form" class="space-y-8"> | |
| <!-- 1. Tab General --> | |
| <div id="tab-general" class="tab-content space-y-6"> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Tên Website</label> | |
| <input type="text" id="site_name" name="site_name" placeholder="Ví dụ: TPHIMX" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| <p class="text-[11px] text-gray-500 font-medium">Hiển thị ở hậu tố tiêu đề các trang (Ví dụ: Trang chủ | TPHIMX).</p> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">URL Website</label> | |
| <input type="url" id="site_url" name="site_url" placeholder="Ví dụ: https://tphimx.com" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| <p class="text-[11px] text-gray-500 font-medium">Đường dẫn đầy đủ dùng để sinh link xác minh tài khoản, khôi phục mật khẩu.</p> | |
| </div> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Mô tả Website (SEO Description)</label> | |
| <textarea id="site_description" name="site_description" rows="3" placeholder="Mô tả website..." class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all resize-none"></textarea> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Từ khóa Website (SEO Keywords)</label> | |
| <input type="text" id="site_keywords" name="site_keywords" placeholder="Ví dụ: xem phim online, phim vietsub, phim hd, phim moi, phim bo" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| <p class="text-[11px] text-gray-500 font-medium">Các từ khóa cách nhau bởi dấu phẩy, giúp các công cụ tìm kiếm dễ dàng tìm thấy website.</p> | |
| </div> | |
| </div> | |
| <!-- 2. Tab SMTP --> | |
| <div id="tab-smtp" class="tab-content hidden space-y-6"> | |
| <div class="grid gap-6 md:grid-cols-3"> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">SMTP Host</label> | |
| <input type="text" id="smtp_host" name="smtp_host" placeholder="smtp.gmail.com" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">SMTP Port</label> | |
| <input type="number" id="smtp_port" name="smtp_port" placeholder="465" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Bảo mật SMTP</label> | |
| <select id="smtp_secure" name="smtp_secure" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all cursor-pointer"> | |
| <option value="SSL">SSL (Khuyên dùng - Port 465)</option> | |
| <option value="TLS">TLS (Port 587)</option> | |
| <option value="NONE">Không bảo mật</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Tài khoản SMTP</label> | |
| <input type="text" id="smtp_user" name="smtp_user" placeholder="gmail_cua_ban@gmail.com" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Mật khẩu ứng dụng SMTP</label> | |
| <input type="password" id="smtp_pass" name="smtp_pass" placeholder="••••••••••••••••" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Email người gửi</label> | |
| <input type="email" id="smtp_from_email" name="smtp_from_email" placeholder="no-reply@tphimx.com" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Tên người gửi</label> | |
| <input type="text" id="smtp_from_name" name="smtp_from_name" placeholder="TPHIMX" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <div class="pt-4 border-t border-white/5 flex justify-end"> | |
| <button type="button" id="btn-test-smtp" class="flex items-center gap-2 rounded-2xl bg-emerald-500/10 hover:bg-emerald-500 text-emerald-400 hover:text-white border border-emerald-500/20 px-6 py-3.5 text-xs font-black uppercase tracking-widest transition-all active:scale-95 cursor-pointer"> | |
| <i class="fas fa-paper-plane"></i> Gửi email test | |
| </button> | |
| </div> | |
| </div> | |
| <!-- 3. Tab User --> | |
| <div id="tab-user" class="tab-content hidden space-y-6"> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <!-- Cho phép đăng ký --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white">Cho phép Đăng ký tài khoản</h3> | |
| <p class="text-xs text-gray-500 mt-1">Cho phép khách truy cập đăng ký tài khoản mới trên trang web.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="allow_registration" name="allow_registration" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <!-- Xác minh email đăng ký --> | |
| <div id="verify-email-box" class="glass-sub rounded-3xl p-6 border border-white/5 flex items-center justify-between transition-all duration-300"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white">Bắt buộc Xác minh Email</h3> | |
| <p class="text-xs text-gray-500 mt-1">Yêu cầu xác nhận qua email trước khi tài khoản được phép sử dụng.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="require_email_verification" name="require_email_verification" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <!-- Thời gian hết hạn của token xác minh mail (giây) --> | |
| <div id="expiry-verification-box" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Hết hạn Token Xác minh Email (Giây)</label> | |
| <input type="number" id="verification_token_expiry" name="verification_token_expiry" placeholder="3600" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| <p class="text-[11px] text-gray-500 font-medium">Thời gian mã xác minh có hiệu lực. Mặc định 3600 giây (1 giờ).</p> | |
| </div> | |
| <!-- Thời gian hết hạn của token quên mật khẩu (giây) --> | |
| <div class="space-y-2"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Hết hạn Token Quên mật khẩu (Giây)</label> | |
| <input type="number" id="reset_password_token_expiry" name="reset_password_token_expiry" placeholder="1800" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| <p class="text-[11px] text-gray-500 font-medium">Thời gian link reset mật khẩu có hiệu lực. Mặc định 1800 giây (30 phút).</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 4. Tab Social --> | |
| <div id="tab-social" class="tab-content hidden space-y-6"> | |
| <div class="grid gap-6 md:grid-cols-2"> | |
| <!-- Facebook Page --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fab fa-facebook text-[#1877F2]"></i> Trang Facebook (Page) | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link Facebook Page dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_fb_enable" name="social_fb_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_fb_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn Facebook Page</label> | |
| <input type="url" id="social_fb_url" name="social_fb_url" placeholder="https://www.facebook.com/tphimx" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <!-- Facebook Group --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fas fa-users text-[#1877F2]"></i> Nhóm Facebook (Group) | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link Facebook Group dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_fb_group_enable" name="social_fb_group_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_fb_group_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn Facebook Group</label> | |
| <input type="url" id="social_fb_group_url" name="social_fb_group_url" placeholder="https://www.facebook.com/groups/1819522938713878" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <!-- Zalo Group --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fas fa-comments text-[#0068FF]"></i> Nhóm Zalo (Group) | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link Zalo Group dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_zalo_group_enable" name="social_zalo_group_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_zalo_group_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn Zalo Group</label> | |
| <input type="url" id="social_zalo_group_url" name="social_zalo_group_url" placeholder="https://zalo.me/g/nc4aaozaxnr5fszvnxdb" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <!-- Telegram --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fab fa-telegram text-[#24A1DE]"></i> Telegram Channel / Contact | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link Telegram dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_telegram_enable" name="social_telegram_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_telegram_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn Telegram</label> | |
| <input type="url" id="social_telegram_url" name="social_telegram_url" placeholder="https://t.me/yourchannel" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <!-- Tiktok --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fab fa-tiktok text-white"></i> Kênh TikTok | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link TikTok dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_tiktok_enable" name="social_tiktok_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_tiktok_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn TikTok</label> | |
| <input type="url" id="social_tiktok_url" name="social_tiktok_url" placeholder="https://www.tiktok.com/@yourchannel" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| <!-- Instagram --> | |
| <div class="glass-sub rounded-3xl p-6 border border-white/5 space-y-4"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <h3 class="text-sm font-bold text-white flex items-center gap-2"> | |
| <i class="fab fa-instagram text-[#E1306C]"></i> Kênh Instagram | |
| </h3> | |
| <p class="text-xs text-gray-500 mt-1">Bật để hiển thị biểu tượng link Instagram dưới footer.</p> | |
| </div> | |
| <label class="relative inline-flex items-center cursor-pointer select-none"> | |
| <input type="checkbox" id="social_instagram_enable" name="social_instagram_enable" class="sr-only peer" /> | |
| <div class="w-11 h-6 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-gray-300 after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary"></div> | |
| </label> | |
| </div> | |
| <div id="box-social_instagram_url" class="space-y-2 transition-all duration-300"> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500">Đường dẫn Instagram</label> | |
| <input type="url" id="social_instagram_url" name="social_instagram_url" placeholder="https://www.instagram.com/yourusername" class="w-full bg-[#12141d] border border-white/5 rounded-2xl px-5 py-4 text-sm text-white focus:outline-none focus:border-primary/50 transition-all" /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Submit Button --> | |
| <div class="pt-8 border-t border-white/5 flex justify-end"> | |
| <button type="submit" class="flex items-center gap-2 rounded-2xl bg-primary px-8 py-4 text-xs font-black uppercase tracking-widest text-dark hover:scale-105 transition-all shadow-[0_0_20px_var(--primary)] cursor-pointer"> | |
| <i class="fas fa-save"></i> Lưu cài đặt | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </AdminLayout> | |
| <script is:inline define:vars={{ initialSettings: settings }}> | |
| let currentTab = 'general'; | |
| function checkSmtpTestButtonStatus() { | |
| const testSmtpBtn = document.getElementById('btn-test-smtp'); | |
| if (!testSmtpBtn) return; | |
| const userVal = document.getElementById('smtp_user')?.value?.trim() || ''; | |
| const passVal = document.getElementById('smtp_pass')?.value?.trim() || ''; | |
| const isModified = userVal !== (initialSettings.smtp_user || '') || passVal !== (initialSettings.smtp_pass || ''); | |
| if (!initialSettings.smtp_user || !initialSettings.smtp_pass) { | |
| testSmtpBtn.setAttribute('disabled', 'true'); | |
| testSmtpBtn.style.opacity = '0.4'; | |
| testSmtpBtn.style.cursor = 'not-allowed'; | |
| testSmtpBtn.setAttribute('data-txatooltip', 'Vui lòng nhập tài khoản và mật khẩu SMTP rồi bấm Lưu cài đặt trước khi gửi thử!'); | |
| } else if (isModified) { | |
| testSmtpBtn.setAttribute('disabled', 'true'); | |
| testSmtpBtn.style.opacity = '0.4'; | |
| testSmtpBtn.style.cursor = 'not-allowed'; | |
| testSmtpBtn.setAttribute('data-txatooltip', 'Có cấu hình SMTP thay đổi chưa lưu! Vui lòng bấm Lưu cài đặt trước khi gửi thử.'); | |
| } else { | |
| testSmtpBtn.removeAttribute('disabled'); | |
| testSmtpBtn.style.opacity = ''; | |
| testSmtpBtn.style.cursor = 'pointer'; | |
| testSmtpBtn.removeAttribute('data-txatooltip'); | |
| } | |
| if (window.initTxaTooltips) { | |
| window.initTxaTooltips(); | |
| } | |
| } | |
| function initTabs() { | |
| const tabs = ['general', 'smtp', 'user', 'social']; | |
| tabs.forEach(tab => { | |
| const btn = document.getElementById(`btn-tab-${tab}`); | |
| const content = document.getElementById(`tab-${tab}`); | |
| if (btn && content) { | |
| btn.onclick = (e) => { | |
| e.preventDefault(); | |
| // Hide all contents & remove active class from all buttons | |
| tabs.forEach(t => { | |
| const b = document.getElementById(`btn-tab-${t}`); | |
| const c = document.getElementById(`tab-${t}`); | |
| b?.classList.remove('active-tab'); | |
| c?.classList.add('hidden'); | |
| }); | |
| // Show clicked tab | |
| btn.classList.add('active-tab'); | |
| content.classList.remove('hidden'); | |
| currentTab = tab; | |
| }; | |
| } | |
| }); | |
| } | |
| function fillForm() { | |
| const form = document.getElementById('settings-form'); | |
| if (!form) return; | |
| // Text inputs | |
| const keys = [ | |
| 'site_name', 'site_url', 'site_description', 'site_keywords', | |
| 'smtp_host', 'smtp_port', 'smtp_secure', 'smtp_user', 'smtp_pass', 'smtp_from_email', 'smtp_from_name', | |
| 'verification_token_expiry', 'reset_password_token_expiry', | |
| 'social_fb_url', 'social_fb_group_url', 'social_zalo_group_url', 'social_telegram_url', 'social_tiktok_url', 'social_instagram_url' | |
| ]; | |
| keys.forEach(key => { | |
| const input = document.getElementById(key); | |
| if (input && initialSettings[key] !== undefined) { | |
| input.value = initialSettings[key]; | |
| } | |
| }); | |
| // Checkbox inputs (Boolean) | |
| const boolKeys = [ | |
| 'allow_registration', 'require_email_verification', | |
| 'social_fb_enable', 'social_fb_group_enable', 'social_zalo_group_enable', 'social_telegram_enable', 'social_tiktok_enable', 'social_instagram_enable' | |
| ]; | |
| boolKeys.forEach(key => { | |
| const input = document.getElementById(key); | |
| if (input && initialSettings[key] !== undefined) { | |
| input.checked = initialSettings[key]; | |
| } | |
| }); | |
| updateVisibility(); | |
| checkSmtpTestButtonStatus(); | |
| } | |
| function updateVisibility() { | |
| const allowReg = document.getElementById('allow_registration'); | |
| const verifyBox = document.getElementById('verify-email-box'); | |
| const requireVerify = document.getElementById('require_email_verification'); | |
| const expiryBox = document.getElementById('expiry-verification-box'); | |
| // 1. Cho phép đăng ký | |
| if (allowReg && verifyBox) { | |
| if (allowReg.checked) { | |
| verifyBox.classList.remove('opacity-40', 'pointer-events-none'); | |
| const chk = verifyBox.querySelector('input'); | |
| if (chk) chk.disabled = false; | |
| } else { | |
| verifyBox.classList.add('opacity-40', 'pointer-events-none'); | |
| const chk = verifyBox.querySelector('input'); | |
| if (chk) { | |
| chk.checked = false; | |
| chk.disabled = true; | |
| } | |
| } | |
| } | |
| // 2. Bắt buộc xác minh email | |
| if (requireVerify && expiryBox) { | |
| if (requireVerify.checked && (!allowReg || allowReg.checked)) { | |
| expiryBox.classList.remove('opacity-40', 'pointer-events-none'); | |
| const inp = expiryBox.querySelector('input'); | |
| if (inp) inp.disabled = false; | |
| } else { | |
| expiryBox.classList.add('opacity-40', 'pointer-events-none'); | |
| const inp = expiryBox.querySelector('input'); | |
| if (inp) inp.disabled = true; | |
| } | |
| } | |
| // 3. Mạng xã hội (Ẩn ô nhập link khi chưa bật) | |
| const socials = ['social_fb', 'social_fb_group', 'social_zalo_group', 'social_telegram', 'social_tiktok', 'social_instagram']; | |
| socials.forEach(s => { | |
| const chk = document.getElementById(`${s}_enable`); | |
| const box = document.getElementById(`box-${s}_url`); | |
| if (chk && box) { | |
| if (chk.checked) { | |
| box.classList.remove('hidden'); | |
| const inp = box.querySelector('input'); | |
| if (inp) inp.disabled = false; | |
| } else { | |
| box.classList.add('hidden'); | |
| const inp = box.querySelector('input'); | |
| if (inp) inp.disabled = true; | |
| } | |
| } | |
| }); | |
| } | |
| function setupEventListeners() { | |
| const form = document.getElementById('settings-form'); | |
| // Toggle change events | |
| const allowReg = document.getElementById('allow_registration'); | |
| const requireVerify = document.getElementById('require_email_verification'); | |
| allowReg?.addEventListener('change', updateVisibility); | |
| requireVerify?.addEventListener('change', updateVisibility); | |
| // Watch for Social Switches change events | |
| const socials = ['social_fb', 'social_fb_group', 'social_zalo_group', 'social_telegram', 'social_tiktok', 'social_instagram']; | |
| socials.forEach(s => { | |
| document.getElementById(`${s}_enable`)?.addEventListener('change', updateVisibility); | |
| }); | |
| // Watch for input changes to dynamically disable SMTP test button if unsaved changes exist | |
| document.getElementById('smtp_user')?.addEventListener('input', checkSmtpTestButtonStatus); | |
| document.getElementById('smtp_pass')?.addEventListener('input', checkSmtpTestButtonStatus); | |
| // Save Settings | |
| form?.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const btn = form.querySelector('button[type="submit"]'); | |
| if (btn) btn.disabled = true; | |
| const formData = {}; | |
| // Text and Select elements | |
| const inputs = form.querySelectorAll('input[type="text"], input[type="url"], input[type="number"], input[type="password"], textarea, select'); | |
| inputs.forEach(inp => { | |
| const name = inp.getAttribute('name'); | |
| if (name) { | |
| let val = inp.value; | |
| if (inp.getAttribute('type') === 'number') { | |
| val = Number(val); | |
| } | |
| formData[name] = val; | |
| } | |
| }); | |
| // Toggles | |
| const checkboxes = form.querySelectorAll('input[type="checkbox"]'); | |
| checkboxes.forEach(chk => { | |
| const name = chk.getAttribute('name'); | |
| if (name) { | |
| formData[name] = chk.checked; | |
| } | |
| }); | |
| try { | |
| const res = await fetch('/api/admin/settings', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(formData) | |
| }); | |
| const data = await res.json(); | |
| if (res.ok) { | |
| window.txatoast.success(data.message); | |
| // Cập nhật lại initialSettings và trạng thái nút test SMTP | |
| initialSettings.smtp_user = formData.smtp_user; | |
| initialSettings.smtp_pass = formData.smtp_pass; | |
| checkSmtpTestButtonStatus(); | |
| } else { | |
| window.txatoast.error(data.error); | |
| } | |
| } catch(e) { | |
| window.txatoast.error('Lỗi lưu cấu hình máy chủ!'); | |
| } finally { | |
| if (btn) btn.disabled = false; | |
| } | |
| }); | |
| // Test SMTP Email connection | |
| const testSmtpBtn = document.getElementById('btn-test-smtp'); | |
| testSmtpBtn?.addEventListener('click', () => { | |
| // Gather current smtp settings to test connection before save | |
| const smtpOptions = { | |
| smtp_host: document.getElementById('smtp_host').value.trim(), | |
| smtp_port: Number(document.getElementById('smtp_port').value.trim()), | |
| smtp_secure: document.getElementById('smtp_secure').value, | |
| smtp_user: document.getElementById('smtp_user').value.trim(), | |
| smtp_pass: document.getElementById('smtp_pass').value.trim(), | |
| smtp_from_email: document.getElementById('smtp_from_email').value.trim(), | |
| smtp_from_name: document.getElementById('smtp_from_name').value.trim(), | |
| site_url: document.getElementById('site_url').value.trim(), | |
| site_name: document.getElementById('site_name').value.trim() | |
| }; | |
| if (!smtpOptions.smtp_host || !smtpOptions.smtp_port || !smtpOptions.smtp_user || !smtpOptions.smtp_pass) { | |
| window.txatoast.warning('Vui lòng điền đầy đủ cấu hình SMTP (Host, Port, Tài khoản, Mật khẩu) trước khi gửi thử!'); | |
| return; | |
| } | |
| window.txamodal.show({ | |
| title: 'Gửi thử Email Test', | |
| message: ` | |
| <div class="space-y-4 text-left"> | |
| <p class="text-xs text-gray-500">Mã kiểm thử sẽ gửi một Email thử nghiệm mẫu bằng cấu hình bạn đang điền phía trên.</p> | |
| <div> | |
| <label class="block text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Địa chỉ Email nhận</label> | |
| <input type="email" id="test-recipient-email" placeholder="vi_du@gmail.com" class="w-full bg-[#12141d] border border-white/5 rounded-xl px-4 py-2.5 text-sm text-white focus:outline-none focus:border-primary/50" /> | |
| </div> | |
| </div>`, | |
| type: 'success', | |
| confirmText: 'Gửi ngay', | |
| onConfirm: async () => { | |
| const testEmailInput = document.getElementById('test-recipient-email'); | |
| const testEmail = testEmailInput ? testEmailInput.value.trim() : ''; | |
| if (!testEmail) { | |
| window.txatoast.error('Email nhận không được để trống!'); | |
| return false; | |
| } | |
| // Nút đổi trạng thái (Loading state) | |
| const confirmBtn = document.getElementById('txamodal-confirm'); | |
| const cancelBtn = document.getElementById('txamodal-cancel'); | |
| const originalConfirmHtml = confirmBtn ? confirmBtn.innerHTML : 'Gửi ngay'; | |
| if (confirmBtn) { | |
| confirmBtn.innerHTML = '<i class="fas fa-circle-notch fa-spin mr-2"></i> Đang gửi...'; | |
| confirmBtn.disabled = true; | |
| confirmBtn.style.opacity = '0.6'; | |
| confirmBtn.style.cursor = 'not-allowed'; | |
| } | |
| if (cancelBtn) { | |
| cancelBtn.disabled = true; | |
| cancelBtn.style.opacity = '0.5'; | |
| cancelBtn.style.cursor = 'not-allowed'; | |
| } | |
| window.txatoast.info('Đang bắt đầu gửi thư thử nghiệm...'); | |
| try { | |
| const res = await fetch('/api/admin/test-email', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| toEmail: testEmail, | |
| smtpOptions | |
| }) | |
| }); | |
| const data = await res.json(); | |
| if (res.ok) { | |
| window.txatoast.success(data.message); | |
| // Reset state lại nút và đóng modal | |
| if (confirmBtn) { | |
| confirmBtn.innerHTML = originalConfirmHtml; | |
| confirmBtn.disabled = false; | |
| confirmBtn.style.opacity = ''; | |
| confirmBtn.style.cursor = 'pointer'; | |
| } | |
| if (cancelBtn) { | |
| cancelBtn.disabled = false; | |
| cancelBtn.style.opacity = ''; | |
| cancelBtn.style.cursor = 'pointer'; | |
| } | |
| window.txamodal.hide(); | |
| return true; | |
| } else { | |
| window.txatoast.error(data.error); | |
| // Reset state lại nút (không đóng modal để họ sửa lại email hoặc kiểm tra lại cấu hình) | |
| if (confirmBtn) { | |
| confirmBtn.innerHTML = originalConfirmHtml; | |
| confirmBtn.disabled = false; | |
| confirmBtn.style.opacity = ''; | |
| confirmBtn.style.cursor = 'pointer'; | |
| } | |
| if (cancelBtn) { | |
| cancelBtn.disabled = false; | |
| cancelBtn.style.opacity = ''; | |
| cancelBtn.style.cursor = 'pointer'; | |
| } | |
| return false; | |
| } | |
| } catch(e) { | |
| window.txatoast.error('Lỗi kết nối máy chủ gửi thử!'); | |
| // Reset state lại nút | |
| if (confirmBtn) { | |
| confirmBtn.innerHTML = originalConfirmHtml; | |
| confirmBtn.disabled = false; | |
| confirmBtn.style.opacity = ''; | |
| confirmBtn.style.cursor = 'pointer'; | |
| } | |
| if (cancelBtn) { | |
| cancelBtn.disabled = false; | |
| cancelBtn.style.opacity = ''; | |
| cancelBtn.style.cursor = 'pointer'; | |
| } | |
| return false; | |
| } | |
| } | |
| }); | |
| }); | |
| } | |
| function init() { | |
| initTabs(); | |
| fillForm(); | |
| setupEventListeners(); | |
| } | |
| document.addEventListener('astro:page-load', init); | |
| </script> | |
| <style> | |
| .glass { | |
| background: rgba(18, 20, 29, 0.4); | |
| backdrop-filter: blur(40px) saturate(200%); | |
| } | |
| .glass-sub { | |
| background: rgba(255, 255, 255, 0.02); | |
| } | |
| .tab-btn { | |
| color: #6b7280; | |
| border: 1px solid rgba(255, 255, 255, 0.05); | |
| background: rgba(255, 255, 255, 0.02); | |
| } | |
| .tab-btn:hover { | |
| color: #ffffff; | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .active-tab { | |
| background: #7c3aed !important; | |
| color: #ffffff !important; | |
| border-color: #7c3aed !important; | |
| box-shadow: 0 0 15px rgba(124, 58, 237, 0.4); | |
| } | |
| </style> | |