// Dynamically load SweetAlert2 script and style from CDN if (typeof Swal === 'undefined' && !window.Swal) { const swalStyle = document.createElement('link'); swalStyle.rel = 'stylesheet'; swalStyle.href = 'https://cdn.jsdelivr.net/npm/sweetalert2@11.7.3/dist/sweetalert2.min.css'; document.head.appendChild(swalStyle); const swalScript = document.createElement('script'); swalScript.src = 'https://cdn.jsdelivr.net/npm/sweetalert2@11.7.3/dist/sweetalert2.all.min.js'; swalScript.async = true; document.head.appendChild(swalScript); } // Global SweetAlert2 standard helper to replace default browser alert/confirm window.actionAlert = window.alertConfirm = { fire: ({ icon, title, text }) => { const sw = typeof Swal !== 'undefined' ? Swal : (window.Swal || null); if (sw) { sw.fire({ icon: icon || 'info', title: title, text: text, confirmButtonColor: '#7c3aed', confirmButtonText: 'OK', customClass: { popup: 'rounded-12' } }); } else { alert(title || text); } }, confirm: (title, text, isVi) => { const sw = typeof Swal !== 'undefined' ? Swal : (window.Swal || null); if (sw) { return sw.fire({ title: title, text: text, icon: 'question', showCancelButton: true, confirmButtonColor: '#7c3aed', cancelButtonColor: '#64748b', confirmButtonText: isVi ? 'Xác nhận' : 'Confirm', cancelButtonText: isVi ? 'Hủy' : 'Cancel', customClass: { popup: 'rounded-12' } }).then(result => !!result.isConfirmed); } else { return Promise.resolve(confirm(title + (text ? '\n' + text : ''))); } } }; // 1. Đảm bảo React đã sẵn sàng const { useState, useEffect, useRef, memo, useMemo, useCallback } = React || {}; // 2. Kiểm tra ReactRouterDOM trước khi giải nén (Destructuring) // Nếu chưa có, gán tạm thành object rỗng để không bị lỗi "cannot destructure of undefined" const RRD = window.ReactRouterDOM || {}; const { BrowserRouter, HashRouter, // Thêm HashRouter nếu bạn dùng cho môi trường không có server rewrite useLocation, useHistory, Route, Switch, Link, NavLink, Redirect } = RRD; // 3. Kiểm tra xem các thành phần quan trọng có tồn tại không if (!RRD.Route) { console.warn("ReactRouterDOM chưa được nạp. Đang chờ các script khác..."); } const LoadComponent = async (path) => { const finalPath = window.isProd ? path.replace('.jsx', '.js') : path; return fetch(finalPath) .then(response => { if (!response.ok) throw new Error(`Lỗi tải: ${finalPath}`); return response.text(); }) .then(code => { let executableCode = code; // Nếu là Develop (.jsx), biên dịch qua Babel if (finalPath.split('?')[0].endsWith('.jsx')) { executableCode = Babel.transform(code, { presets: ['es2015', 'react'] }).code; } // --- ĐOẠN QUAN TRỌNG NHẤT ĐỂ SỬA LỖI --- const wrappedCode = ` const exports = {}; const module = { exports }; ${executableCode} return (module.exports.default || module.exports || exports.default || exports); `; // Truyền React vào làm tham số để component bên trong có thể sử dụng return new Function('React', 'ReactDOM', wrappedCode)(React, ReactDOM); }) .catch(err => console.error("Loader Error:", err)); }; const LoadMultipleComponents = async (paths) => { const promises = paths.map(p => LoadComponent(p)); const components = await Promise.all(promises); return paths.reduce((acc, path, index) => { const name = path.split('?')[0].split('/').pop().replace('.jsx', '').replace('.js', ''); acc[name] = components[index]; return acc; }, {}); };