From f3d48e8837d83421415f8b3d76d64d5f760cad12 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Wed, 22 Jul 2026 19:43:04 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20b=E1=BB=95=20sung=20ph=E1=BA=A7n=20prof?= =?UTF-8?q?ile=20c=E1=BB=A7a=20user=20c=C3=B3=20th=E1=BB=83=20drag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/app.jsx | 49 ++++++++++++++++++++----- app/static/js/app.precompiled.js | 60 ++++++++++++++++++++++++++----- app/storage/sonicforge.db | Bin 45056 -> 45056 bytes 3 files changed, 93 insertions(+), 16 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 74413ae..249e622 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -2743,6 +2743,8 @@ const ProfileModal = ({ const [msg, setMsg] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); + const [dragOfs, setDragOfs] = useState({ x: 0, y: 0 }); + const dragRef = useRef({ active: false, startX: 0, startY: 0, ofsX: 0, ofsY: 0 }); // Projects list state const [projectsList, setProjectsList] = useState([]); @@ -2755,6 +2757,26 @@ const ProfileModal = ({ // Confirmation modal state const [confirmModal, setConfirmModal] = useState(null); + const handleDragStart = (e) => { + const r = dragRef.current; + r.active = true; + r.startX = e.clientX; + r.startY = e.clientY; + r.ofsX = dragOfs.x; + r.ofsY = dragOfs.y; + const onMove = (ev) => { + if (!r.active) return; + setDragOfs({ x: r.ofsX + ev.clientX - r.startX, y: r.ofsY + ev.clientY - r.startY }); + }; + const onUp = () => { + r.active = false; + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; + useEffect(() => { if (isOpen) { fetchProfile(); @@ -2890,10 +2912,18 @@ const ProfileModal = ({ } }; - return /*#__PURE__*/React.createElement("div", { - className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm relative" - }, confirmModal && /*#__PURE__*/React.createElement("div", { - className: "absolute inset-0 z-[60] flex items-center justify-center bg-black/50" + const modalStyle = { + left: `calc(50% + ${dragOfs.x}px)`, + top: `calc(50% + ${dragOfs.y}px)`, + transform: 'translate(-50%, -50%)' + }; + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { + key: "backdrop", + className: "fixed inset-0 z-40 bg-black/70 backdrop-blur-sm", + onClick: onClose + }), confirmModal && /*#__PURE__*/React.createElement("div", { + key: "confirm-overlay", + className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/50" }, /*#__PURE__*/React.createElement("div", { className: "bg-[#262626] border border-[#383838] rounded-lg shadow-2xl p-5 max-w-sm w-full text-slate-200" }, /*#__PURE__*/React.createElement("h4", { @@ -2909,9 +2939,12 @@ const ProfileModal = ({ onClick: () => { const fn = confirmModal.onConfirm; setConfirmModal(null); fn(); }, className: "px-3 py-1.5 bg-rose-700 hover:bg-rose-600 text-white rounded text-xs font-semibold" }, "Xác nhận xóa")))), /*#__PURE__*/React.createElement("div", { - className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]" + key: "dialog", + className: "fixed z-50 bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]", + style: modalStyle }, /*#__PURE__*/React.createElement("div", { - className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0" + className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0 cursor-grab active:cursor-grabbing select-none", + onMouseDown: handleDragStart }, /*#__PURE__*/React.createElement("h3", { className: "text-md font-bold text-teal-400 flex items-center gap-1.5" }, "👤 Hồ Sơ Cá Nhân & Quản Lý Dự Án"), /*#__PURE__*/React.createElement("button", { @@ -8514,7 +8547,7 @@ const App = () => { }, [showAIPanel, showExportPanel, showSelectionPanel, showPythonToolsPanel, showMediaExplorer, showFxRack, showMidiEvents, panelPositions, rightSidebarWidth, mediaExplorerHeight, selectedProviderId, currentUser]); - return /*#__PURE__*/React.createElement("div", { + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: "h-full w-full flex flex-col bg-[#1e1e1e]" }, /*#__PURE__*/React.createElement("header", { className: "h-9 bg-[#2e2e2e] border-b border-[#181818] flex items-center px-1 shrink-0 select-none" @@ -10982,7 +11015,7 @@ const App = () => { setToastMessage(null); }, className: "ml-2 px-2 py-0.5 bg-purple-700 hover:bg-purple-600 text-white rounded text-[10px] uppercase font-bold" - }, toastMessage.actionText || 'Tải về')), /*#__PURE__*/React.createElement(AuthModal, { + }, toastMessage.actionText || 'Tải về'))), /*#__PURE__*/React.createElement(AuthModal, { isOpen: authModalOpen, mode: authMode, forceMandatory: isMandatoryLogin, diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 18d6913..3dae6e1 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -2780,6 +2780,17 @@ const ProfileModal = ({ const [msg, setMsg] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); + const [dragOfs, setDragOfs] = useState({ + x: 0, + y: 0 + }); + const dragRef = useRef({ + active: false, + startX: 0, + startY: 0, + ofsX: 0, + ofsY: 0 + }); // Projects list state const [projectsList, setProjectsList] = useState([]); @@ -2791,6 +2802,28 @@ const ProfileModal = ({ // Confirmation modal state const [confirmModal, setConfirmModal] = useState(null); + const handleDragStart = e => { + const r = dragRef.current; + r.active = true; + r.startX = e.clientX; + r.startY = e.clientY; + r.ofsX = dragOfs.x; + r.ofsY = dragOfs.y; + const onMove = ev => { + if (!r.active) return; + setDragOfs({ + x: r.ofsX + ev.clientX - r.startX, + y: r.ofsY + ev.clientY - r.startY + }); + }; + const onUp = () => { + r.active = false; + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }; useEffect(() => { if (isOpen) { fetchProfile(); @@ -2917,10 +2950,18 @@ const ProfileModal = ({ setLoading(false); } }; - return /*#__PURE__*/React.createElement("div", { - className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm relative" - }, confirmModal && /*#__PURE__*/React.createElement("div", { - className: "absolute inset-0 z-[60] flex items-center justify-center bg-black/50" + const modalStyle = { + left: `calc(50% + ${dragOfs.x}px)`, + top: `calc(50% + ${dragOfs.y}px)`, + transform: 'translate(-50%, -50%)' + }; + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { + key: "backdrop", + className: "fixed inset-0 z-40 bg-black/70 backdrop-blur-sm", + onClick: onClose + }), confirmModal && /*#__PURE__*/React.createElement("div", { + key: "confirm-overlay", + className: "fixed inset-0 z-[60] flex items-center justify-center bg-black/50" }, /*#__PURE__*/React.createElement("div", { className: "bg-[#262626] border border-[#383838] rounded-lg shadow-2xl p-5 max-w-sm w-full text-slate-200" }, /*#__PURE__*/React.createElement("h4", { @@ -2940,9 +2981,12 @@ const ProfileModal = ({ }, className: "px-3 py-1.5 bg-rose-700 hover:bg-rose-600 text-white rounded text-xs font-semibold" }, "Xác nhận xóa")))), /*#__PURE__*/React.createElement("div", { - className: "bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]" + key: "dialog", + className: "fixed z-50 bg-[#262626] border border-[#383838] rounded-xl shadow-2xl w-full max-w-2xl p-6 text-slate-200 flex flex-col max-h-[85vh]", + style: modalStyle }, /*#__PURE__*/React.createElement("div", { - className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0" + className: "flex justify-between items-center pb-3 border-b border-[#383838] shrink-0 cursor-grab active:cursor-grabbing select-none", + onMouseDown: handleDragStart }, /*#__PURE__*/React.createElement("h3", { className: "text-md font-bold text-teal-400 flex items-center gap-1.5" }, "👤 Hồ Sơ Cá Nhân & Quản Lý Dự Án"), /*#__PURE__*/React.createElement("button", { @@ -8994,7 +9038,7 @@ const App = () => { }, 2000); return () => clearTimeout(timer); }, [showAIPanel, showExportPanel, showSelectionPanel, showPythonToolsPanel, showMediaExplorer, showFxRack, showMidiEvents, panelPositions, rightSidebarWidth, mediaExplorerHeight, selectedProviderId, currentUser]); - return /*#__PURE__*/React.createElement("div", { + return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: "h-full w-full flex flex-col bg-[#1e1e1e]" }, /*#__PURE__*/React.createElement("header", { className: "h-9 bg-[#2e2e2e] border-b border-[#181818] flex items-center px-1 shrink-0 select-none" @@ -11530,7 +11574,7 @@ const App = () => { setToastMessage(null); }, className: "ml-2 px-2 py-0.5 bg-purple-700 hover:bg-purple-600 text-white rounded text-[10px] uppercase font-bold" - }, toastMessage.actionText || 'Tải về')), /*#__PURE__*/React.createElement(AuthModal, { + }, toastMessage.actionText || 'Tải về'))), /*#__PURE__*/React.createElement(AuthModal, { isOpen: authModalOpen, mode: authMode, forceMandatory: isMandatoryLogin, diff --git a/app/storage/sonicforge.db b/app/storage/sonicforge.db index 82ddacc0038fff4156465be6b551f6338c261f1f..52340cf0e99fa3e526a70ed95108d6969b109452 100644 GIT binary patch delta 209 zcmZp8z|`=7X@WH4iHS1Kj3+iGEX)^WU%|l4+Q7iSo_7~(0}O6#%wiR6QRU%`54CSg z~wlysEx5_405qLVMwOKz60%3zey zQ7SG?3Q0^VRJ`;23=C{S8yor9Ht(wSU=opGVCMVG!1tN|9sdCs k+$^YYmv8dtK24xnh9wgl7jWt_$1}p6!N{}uUB8e508}(c7ytkO delta 804 zcma))PiqrF7>D-{X1sAog5< z2sZ&EmJJ|5hUH@7W1q*v?Q5k?-fHh?bx$j6%_w}v-S%UhP(%!f5u~=M6}5nE@nk$t z#_Qdy`3j5LVHgxDtjVKR;cgf#0`2E(;cQ=i1te)AEoKgRg1P4)i?(@=2e3(vr zj-dlwta~7NnFkz%UEo67LYNT3EqbbtZWDrS)B4|Myxt8&yxQvJ?hm5WG0n4#q#@G) zj+GpU`w%cg_W^~T>l)0Zrfr=?Gf>jwi8O!z$DctYAJZI{>5hZ_goZ7ED5Jp9brVqE z_iYGuN||#y4Z_6Eh9}UJ!rxyX>w+Md!~WWM(zzpDJ`|R+GeKR>&eW>BQ# t@H-pN_E&m0m~pI