diff --git a/app/globals.css b/app/globals.css index bfc574c..dd6c118 100644 --- a/app/globals.css +++ b/app/globals.css @@ -14,14 +14,15 @@ box-sizing: border-box; } -html, body { +html, +body { height: 100%; } body { margin: 0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; - background: radial-gradient(1200px 600px at 10% -10%, rgba(96,165,250,0.15), transparent 40%) , radial-gradient(1000px 500px at 90% 0%, rgba(34,211,238,0.12), transparent 45%), var(--bg); + background: radial-gradient(1200px 600px at 10% -10%, rgba(96, 165, 250, 0.15), transparent 40%), radial-gradient(1000px 500px at 90% 0%, rgba(34, 211, 238, 0.12), transparent 45%), var(--bg); color: var(--text); } @@ -32,10 +33,10 @@ body { } .glass { - background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.02)); + background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02)); border: 1px solid var(--border); border-radius: 16px; - box-shadow: 0 8px 30px rgba(0,0,0,0.25); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25); backdrop-filter: blur(8px); } @@ -65,13 +66,29 @@ body { gap: 16px; } -.col-12 { grid-column: span 12; } -.col-6 { grid-column: span 6; } -.col-4 { grid-column: span 4; } -.col-3 { grid-column: span 3; } +.col-12 { + grid-column: span 12; +} + +.col-6 { + grid-column: span 6; +} + +.col-4 { + grid-column: span 4; +} + +.col-3 { + grid-column: span 3; +} @media (max-width: 1024px) { - .col-6, .col-4, .col-3 { grid-column: span 12; } + + .col-6, + .col-4, + .col-3 { + grid-column: span 12; + } } .navbar { @@ -101,6 +118,7 @@ body { .content { padding-top: 90px; } + .navbar { top: 0; left: 0; @@ -110,6 +128,7 @@ body { border-left: none; border-right: none; } + .add-fund-section { margin-top: 60px; } @@ -133,9 +152,10 @@ body { font-size: 14px; transition: border-color 200ms ease, box-shadow 200ms ease; } + .input:focus { border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(96,165,250,0.2); + box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.2); } /* 美化并隐藏原生数字输入的上下按钮,使用自定义按钮 */ @@ -144,6 +164,7 @@ input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } + input[type="number"] { -moz-appearance: textfield; } @@ -159,10 +180,12 @@ input[type="number"] { cursor: pointer; transition: transform 150ms ease, box-shadow 200ms ease; } + .button:hover { transform: translateY(-1px); - box-shadow: 0 10px 20px rgba(34,211,238,0.25); + box-shadow: 0 10px 20px rgba(34, 211, 238, 0.25); } + .button:active { transform: translateY(0); } @@ -200,10 +223,12 @@ input[type="number"] { background: #0b1220; font-size: 12px; } + .badge-v span { font-size: 10px; color: var(--muted); } + .badge-v strong { font-size: 11px; } @@ -213,14 +238,17 @@ input[type="number"] { align-items: baseline; gap: 8px; } + .stat .label { font-size: 12px; color: var(--muted); } + .stat .value { font-size: 20px; font-weight: 700; } + .stat .badge { padding: 4px 8px; font-size: 12px; @@ -228,60 +256,80 @@ input[type="number"] { @media (max-width: 640px) { .input { - font-size: 16px; /* 防止 iOS 在输入时自动放大 */ + font-size: 16px; + /* 防止 iOS 在输入时自动放大 */ } + .container { padding: 16px; } + .grid { gap: 12px; } + .card { padding: 12px; } + .stat { flex-direction: column; gap: 4px; min-width: 0; } + .stat .label { font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + .stat .value { font-size: 15px; line-height: 1.2; white-space: nowrap; } + .stat .badge { padding: 2px 6px; font-size: 13px; width: fit-content; } + .card .title { flex-wrap: wrap; } + .item .name { max-width: 100px; font-size: 14px; } + .item .badge { padding: 2px 4px; font-size: 14px; } } -.up { color: var(--danger); } -.down { color: var(--success); } + +.up { + color: var(--danger); +} + +.down { + color: var(--success); +} .list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; } + @media (max-width: 640px) { - .list { grid-template-columns: 1fr; } + .list { + grid-template-columns: 1fr; + } } .item { @@ -293,6 +341,7 @@ input[type="number"] { border: 1px solid var(--border); background: #0b1220; } + .item .name { overflow: hidden; white-space: nowrap; @@ -306,6 +355,7 @@ input[type="number"] { max-width: none; word-break: break-word; } + .item .weight { font-weight: 600; color: var(--accent); @@ -359,20 +409,24 @@ input[type="number"] { cursor: pointer; transition: box-shadow 200ms ease, border-color 200ms ease, transform 150ms ease, color 200ms ease; } + .icon-button:hover { color: var(--text); transform: translateY(-1px); border-color: var(--accent); } + .icon-button:active { transform: translateY(0); } + .icon-button.danger { background: linear-gradient(180deg, #ef4444, #f87171); color: #2b0b0b; } + .icon-button.danger:hover { - box-shadow: 0 10px 20px rgba(248,113,113,0.25); + box-shadow: 0 10px 20px rgba(248, 113, 113, 0.25); } .fav-button { @@ -385,9 +439,11 @@ input[type="number"] { width: auto; height: auto; } + .fav-button:hover { color: var(--accent); } + .fav-button.active { color: var(--accent); } @@ -456,8 +512,15 @@ input[type="number"] { align-items: center; } -.text-right { text-align: right; justify-content: flex-end; } -.text-center { text-align: center; justify-content: center; } +.text-right { + text-align: right; + justify-content: flex-end; +} + +.text-center { + text-align: center; + justify-content: center; +} .name-cell { gap: 8px; @@ -501,31 +564,58 @@ input[type="number"] { .table-header-row { display: none; } + .table-row { grid-template-columns: 1fr 80px 100px; - grid-template-areas: + grid-template-areas: "name value change" "name time profit"; gap: 4px 12px; padding: 12px !important; } - .name-cell { grid-area: name; } - .value-cell { grid-area: value; } - .change-cell { grid-area: change; } - .profit-cell { grid-area: profit; } - .time-cell { - grid-area: time; + + .name-cell { + grid-area: name; + } + + .value-cell { + grid-area: value; + } + + .change-cell { + grid-area: change; + } + + .profit-cell { + grid-area: profit; + } + + .time-cell { + grid-area: time; justify-content: flex-end; } - .action-cell { display: none; } - .holding-cell { display: none; } - .holding-amount-cell { display: none; } + + .action-cell { + display: none; + } + + .holding-cell { + display: none; + } + + .holding-amount-cell { + display: none; + } + .table-cell.time-cell span { font-size: 10px !important; } } -.stat-compact .up { color: var(--danger); } +.stat-compact .up { + color: var(--danger); +} + @media (max-width: 768px) { .action-cell .danger { display: none; @@ -534,7 +624,8 @@ input[type="number"] { .swipe-action-bg { position: absolute; - top: 1px; /* 留出一点缝隙,或者与 border 对齐 */ + top: 1px; + /* 留出一点缝隙,或者与 border 对齐 */ bottom: 1px; right: 0; width: 80px; @@ -551,7 +642,10 @@ input[type="number"] { cursor: pointer; box-shadow: inset 10px 0 20px -10px rgba(0,0,0,0.2); /* 增加一点内阴影,增加层次感 */ } -.stat-compact .down { color: var(--success); } + +.stat-compact .down { + color: var(--success); +} .filter-bar { transition: all 0.3s ease; @@ -572,9 +666,11 @@ input[type="number"] { flex-direction: column; align-items: center !important; } + .tabs { width: 100%; - justify-content: flex-start; /* 移动端改为左对齐 */ + justify-content: flex-start; + /* 移动端改为左对齐 */ background: transparent; border-radius: 0; backdrop-filter: none; @@ -619,23 +715,26 @@ input[type="number"] { .modal-overlay { position: fixed; inset: 0; - background: rgba(2,6,23,0.6); + background: rgba(2, 6, 23, 0.6); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 60; } + .modal { width: 560px; max-width: 92vw; padding: 16px; } + .chips { display: flex; flex-wrap: wrap; gap: 8px; } + .chip { display: inline-flex; align-items: center; @@ -1051,8 +1150,188 @@ input[type="number"] { width: 100%; margin-bottom: 8px; } - + .tabs { max-width: none !important; } } + +/* ========== 用户菜单样式 ========== */ +.user-menu-container { + position: relative; +} + +.user-menu-trigger { + position: relative; +} + +.user-menu-trigger.logged-in { + border-color: var(--primary); + background: rgba(34, 211, 238, 0.1); +} + +.user-avatar-small { + width: 20px; + height: 20px; + border-radius: 50%; + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #05263b; + font-size: 11px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; +} + +.user-menu-dropdown { + position: fixed; + top: 76px; + right: 16px; + min-width: 200px; + padding: 8px; + z-index: 100; + background: rgba(15, 23, 42, 0.95); + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5); +} + +.user-menu-header { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 8px; +} + +.user-avatar-large { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, var(--primary), var(--accent)); + color: #05263b; + font-size: 18px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.user-info { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.user-email { + font-size: 13px; + font-weight: 600; + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.user-status { + font-size: 11px; + color: var(--success); +} + +.user-menu-divider { + height: 1px; + background: var(--border); + margin: 4px 0; +} + +.user-menu-item { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + padding: 10px 12px; + border: none; + border-radius: 10px; + background: transparent; + color: var(--text); + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.user-menu-item:hover { + background: rgba(255, 255, 255, 0.08); +} + +.user-menu-item.danger { + color: var(--danger); +} + +.user-menu-item.danger:hover { + background: rgba(248, 113, 113, 0.1); +} + +/* ========== 登录模态框样式 ========== */ +.login-modal { + max-width: 400px !important; +} + +.login-message { + padding: 10px 14px; + border-radius: 10px; + font-size: 13px; + display: flex; + align-items: center; + gap: 8px; +} + +.login-message.error { + background: rgba(248, 113, 113, 0.15); + border: 1px solid rgba(248, 113, 113, 0.3); + color: var(--danger); +} + +.login-message.success { + background: rgba(52, 211, 153, 0.15); + border: 1px solid rgba(52, 211, 153, 0.3); + color: var(--success); +} + +.button.secondary { + background: transparent; + border: 1px solid var(--border); + color: var(--text); +} + +.button.secondary:hover { + border-color: var(--muted); + background: rgba(255, 255, 255, 0.05); + box-shadow: none; +} + +/* ========== 移动端响应式 ========== */ +@media (max-width: 640px) { + .user-menu-dropdown { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + min-width: 100%; + border-radius: 20px 20px 0 0; + padding: 16px; + padding-bottom: calc(16px + env(safe-area-inset-bottom)); + } + + .user-menu-header { + padding: 16px 8px; + } + + .user-menu-item { + padding: 14px 12px; + font-size: 15px; + } + + .login-modal { + max-width: 100% !important; + margin: 16px; + } +} diff --git a/app/page.jsx b/app/page.jsx index f4bc7f0..729ecd5 100644 --- a/app/page.jsx +++ b/app/page.jsx @@ -6,6 +6,7 @@ import Announcement from "./components/Announcement"; import zhifubaoImg from "./assets/zhifubao.jpg"; import weixinImg from "./assets/weixin.jpg"; import githubImg from "./assets/github.svg"; +import { supabase } from './lib/supabase'; function PlusIcon(props) { return ( @@ -62,6 +63,44 @@ function SortIcon(props) { ); } +function UserIcon(props) { + return ( + + + + + ); +} + +function LogoutIcon(props) { + return ( + + + + + + ); +} + +function LoginIcon(props) { + return ( + + + + + + ); +} + +function MailIcon(props) { + return ( + + + + + ); +} + function GridIcon(props) { return ( @@ -1686,6 +1725,15 @@ export default function HomePage() { // 视图模式 const [viewMode, setViewMode] = useState('card'); // card, list + // 用户认证状态 + const [user, setUser] = useState(null); + const [userMenuOpen, setUserMenuOpen] = useState(false); + const [loginModalOpen, setLoginModalOpen] = useState(false); + const [loginEmail, setLoginEmail] = useState(''); + const [loginLoading, setLoginLoading] = useState(false); + const [loginError, setLoginError] = useState(''); + const [loginSuccess, setLoginSuccess] = useState(''); + // 反馈弹窗状态 const [feedbackOpen, setFeedbackOpen] = useState(false); const [feedbackNonce, setFeedbackNonce] = useState(0); @@ -2173,6 +2221,91 @@ export default function HomePage() { } catch { } }, []); + // 初始化认证状态监听 + useEffect(() => { + // 获取当前 session + supabase.auth.getSession().then(({ data: { session } }) => { + setUser(session?.user ?? null); + }); + + // 监听认证状态变化 + const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { + setUser(session?.user ?? null); + if (session?.user) { + setLoginModalOpen(false); + setLoginEmail(''); + setLoginSuccess(''); + setLoginError(''); + } + }); + + return () => subscription.unsubscribe(); + }, []); + + // 发送魔术链接邮件 + const handleSendMagicLink = async (e) => { + e.preventDefault(); + setLoginError(''); + setLoginSuccess(''); + + // 简单的邮箱格式验证 + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!loginEmail.trim()) { + setLoginError('请输入邮箱地址'); + return; + } + if (!emailRegex.test(loginEmail.trim())) { + setLoginError('请输入有效的邮箱地址'); + return; + } + + setLoginLoading(true); + try { + const { error } = await supabase.auth.signInWithOtp({ + email: loginEmail.trim(), + options: { + emailRedirectTo: window.location.origin + } + }); + if (error) throw error; + setLoginSuccess('验证邮件已发送,请查收邮箱并点击链接完成登录'); + } catch (err) { + if (err.message?.includes('rate limit')) { + setLoginError('请求过于频繁,请稍后再试'); + } else if (err.message?.includes('network')) { + setLoginError('网络错误,请检查网络连接'); + } else { + setLoginError(err.message || '发送验证邮件失败,请稍后再试'); + } + } finally { + setLoginLoading(false); + } + }; + + // 登出 + const handleLogout = async () => { + try { + await supabase.auth.signOut(); + setUserMenuOpen(false); + } catch (err) { + console.error('登出失败', err); + } + }; + + // 关闭用户菜单(点击外部时) + const userMenuRef = useRef(null); + useEffect(() => { + const handleClickOutside = (event) => { + if (userMenuRef.current && !userMenuRef.current.contains(event.target)) { + setUserMenuOpen(false); + } + }; + if (userMenuOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [userMenuOpen]); + useEffect(() => { if (timerRef.current) clearInterval(timerRef.current); timerRef.current = setInterval(() => { @@ -2994,6 +3127,89 @@ export default function HomePage() { > + + {/* 临时隐藏用户菜单入口 */} + @@ -4061,6 +4277,82 @@ export default function HomePage() { )} + + {/* 登录模态框 */} + {loginModalOpen && ( +
{ + setLoginModalOpen(false); + setLoginError(''); + setLoginSuccess(''); + setLoginEmail(''); + }} + > +
e.stopPropagation()}> +
+ + 邮箱登录 + 使用邮箱验证登录 +
+ +
+
+
+ 输入您的邮箱地址,我们将发送一封验证邮件 +
+ setLoginEmail(e.target.value)} + disabled={loginLoading} + autoFocus + /> +
+ + {loginError && ( +
+ {loginError} +
+ )} + + {loginSuccess && ( +
+ {loginSuccess} +
+ )} + +
+ + +
+
+
+
+ )} ); } diff --git a/package-lock.json b/package-lock.json index 7b7b970..14f9c40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "real-time-fund", "version": "0.1.0", "dependencies": { + "@supabase/supabase-js": "^2.78.0", "framer-motion": "^12.29.2", "next": "14.2.5", "react": "18.3.1", @@ -164,6 +165,85 @@ "node": ">= 10" } }, + "node_modules/@supabase/auth-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/auth-js/-/auth-js-2.78.0.tgz", + "integrity": "sha512-cXDtu1U0LeZj/xfnFoV7yCze37TcbNo8FCxy1FpqhMbB9u9QxxDSW6pA5gm/07Ei7m260Lof4CZx67Cu6DPeig==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "2.6.15", + "tslib": "2.8.1" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/functions-js/-/functions-js-2.78.0.tgz", + "integrity": "sha512-t1jOvArBsOINyqaRee1xJ3gryXLvkBzqnKfi6q3YRzzhJbGS6eXz0pXR5fqmJeB01fLC+1njpf3YhMszdPEF7g==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "2.6.15", + "tslib": "2.8.1" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmmirror.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/postgrest-js/-/postgrest-js-2.78.0.tgz", + "integrity": "sha512-AwhpYlSvJ+PSnPmIK8sHj7NGDyDENYfQGKrMtpVIEzQA2ApUjgpUGxzXWN4Z0wEtLQsvv7g4y9HVad9Hzo1TNA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "2.6.15", + "tslib": "2.8.1" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/realtime-js/-/realtime-js-2.78.0.tgz", + "integrity": "sha512-rCs1zmLe7of7hj4s7G9z8rTqzWuNVtmwDr3FiCRCJFawEoa+RQO1xpZGbdeuVvVmKDyVN6b542Okci+117y/LQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "2.6.15", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/storage-js/-/storage-js-2.78.0.tgz", + "integrity": "sha512-n17P0JbjHOlxqJpkaGFOn97i3EusEKPEbWOpuk1r4t00Wg06B8Z4GUiq0O0n1vUpjiMgJUkLIMuBVp+bEgunzQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "2.6.15", + "tslib": "2.8.1" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.78.0", + "resolved": "https://registry.npmmirror.com/@supabase/supabase-js/-/supabase-js-2.78.0.tgz", + "integrity": "sha512-xYMRNBFmKp2m1gMuwcp/gr/HlfZKqjye1Ib8kJe29XJNsgwsfO/f8skxnWiscFKTlkOKLuBexNgl5L8dzGt6vA==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.78.0", + "@supabase/functions-js": "2.78.0", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "2.78.0", + "@supabase/realtime-js": "2.78.0", + "@supabase/storage-js": "2.78.0" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -180,6 +260,30 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/node": { + "version": "25.2.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.2.1.tgz", + "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.7", + "resolved": "https://registry.npmmirror.com/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -460,11 +564,60 @@ } } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index a0b7911..b5eba54 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "next start" }, "dependencies": { + "@supabase/supabase-js": "^2.78.0", "framer-motion": "^12.29.2", "next": "14.2.5", "react": "18.3.1",