diff --git a/src/components/wallet/InventoryTab.jsx b/src/components/wallet/InventoryTab.jsx
index 710c53f..c3eb772 100644
--- a/src/components/wallet/InventoryTab.jsx
+++ b/src/components/wallet/InventoryTab.jsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react';
+import { useState, useEffect, useCallback } from 'react';
import { getInventory } from '../../api/chain';
import { useToast } from '../toast/useToast';
@@ -7,14 +7,16 @@ export default function InventoryTab() {
const [inventory, setInventory] = useState(null);
const [loading, setLoading] = useState(true);
- useEffect(() => {
- let cancelled = false;
+ const load = useCallback(() => {
+ setLoading(true);
getInventory()
- .then((data) => { if (!cancelled) setInventory(data); })
- .catch(() => { if (!cancelled) toast.error('인벤토리를 불러오지 못했습니다.'); })
- .finally(() => { if (!cancelled) setLoading(false); });
- return () => { cancelled = true; };
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
+ .then(setInventory)
+ .catch(() => toast.error('인벤토리를 불러오지 못했습니다.'))
+ .finally(() => setLoading(false));
+ }, [toast]);
+
+ // eslint-disable-next-line react-hooks/set-state-in-effect
+ useEffect(() => { load(); }, [load]);
if (loading) return
불러오는 중...
;
diff --git a/src/components/wallet/WalletSummary.css b/src/components/wallet/WalletSummary.css
new file mode 100644
index 0000000..689c054
--- /dev/null
+++ b/src/components/wallet/WalletSummary.css
@@ -0,0 +1,51 @@
+.wallet-summary {
+ background: rgba(186, 205, 176, 0.06);
+ border: 1px solid rgba(186, 205, 176, 0.15);
+ border-radius: 10px;
+ padding: 20px 24px;
+ margin-bottom: 32px;
+ cursor: pointer;
+ transition: border-color 0.2s, background 0.2s;
+}
+
+.wallet-summary:hover {
+ border-color: rgba(186, 205, 176, 0.3);
+ background: rgba(186, 205, 176, 0.09);
+}
+
+.wallet-summary-title {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ color: rgba(255, 255, 255, 0.4);
+ letter-spacing: 0.08em;
+ margin: 0 0 12px;
+}
+
+.wallet-summary-balance {
+ font-size: 1.8rem;
+ font-weight: 700;
+ color: #BACDB0;
+ margin: 0;
+}
+
+.wallet-summary-stats {
+ display: flex;
+ gap: 24px;
+ margin-top: 12px;
+}
+
+.wallet-summary-stat {
+ font-size: 0.85rem;
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.wallet-summary-stat strong {
+ color: rgba(255, 255, 255, 0.8);
+}
+
+@media (max-width: 768px) {
+ .wallet-summary-stats {
+ flex-direction: column;
+ gap: 8px;
+ }
+}
diff --git a/src/components/wallet/WalletSummary.jsx b/src/components/wallet/WalletSummary.jsx
index 01d283c..ca4feca 100644
--- a/src/components/wallet/WalletSummary.jsx
+++ b/src/components/wallet/WalletSummary.jsx
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { getBalance, getAssets, getInventory } from '../../api/chain';
-import '../../pages/WalletPage.css';
+import './WalletSummary.css';
export default function WalletSummary() {
const navigate = useNavigate();
diff --git a/src/pages/AdminPage.css b/src/pages/AdminPage.css
index 2e6ef46..e6146b9 100644
--- a/src/pages/AdminPage.css
+++ b/src/pages/AdminPage.css
@@ -147,4 +147,4 @@
.admin-main {
padding: 20px 12px 60px;
}
-}
+}
\ No newline at end of file
diff --git a/src/pages/AuthPage.css b/src/pages/AuthPage.css
index a16860b..77cbe90 100644
--- a/src/pages/AuthPage.css
+++ b/src/pages/AuthPage.css
@@ -151,6 +151,13 @@
border-color: rgba(186, 205, 176, 0.5);
}
+.login-logo {
+ max-width: 120px;
+ height: auto;
+ margin: 0 auto 16px;
+ display: block;
+}
+
.login-back {
display: block;
text-align: center;
diff --git a/src/pages/LoginPage.jsx b/src/pages/LoginPage.jsx
index 8d4316a..5debda0 100644
--- a/src/pages/LoginPage.jsx
+++ b/src/pages/LoginPage.jsx
@@ -14,6 +14,18 @@ export default function LoginPage() {
const location = useLocation();
const justRegistered = location.state?.registered;
+ const handleSSAFYLogin = async () => {
+ try {
+ const data = await getSSAFYLoginURL();
+ if (!data.url || !data.url.startsWith('https://')) {
+ throw new Error('유효하지 않은 로그인 URL입니다.');
+ }
+ window.location.href = data.url;
+ } catch {
+ setError('SSAFY 로그인 URL을 가져올 수 없습니다.');
+ }
+ };
+
const handleSubmit = async (e) => {
e.preventDefault();
setError('');
@@ -84,17 +96,7 @@ export default function LoginPage() {
-

+
One of the plans
MULTIPLAYER
@@ -128,12 +128,10 @@ export default function RegisterPage() {
className={confirmMatch ? 'input-valid' : confirmMismatch ? 'input-invalid' : ''}
aria-describedby="confirm-hint"
/>
- {confirmMismatch && (
-
비밀번호가 일치하지 않습니다
- )}
- {confirmMatch && (
-
비밀번호가 일치합니다 {'\u2713'}
- )}
+
+ {confirmMismatch && '비밀번호가 일치하지 않습니다'}
+ {confirmMatch && `비밀번호가 일치합니다 \u2713`}
+
{error &&
{error}
}
diff --git a/src/pages/SSAFYCallbackPage.jsx b/src/pages/SSAFYCallbackPage.jsx
index f697dae..d693b5b 100644
--- a/src/pages/SSAFYCallbackPage.jsx
+++ b/src/pages/SSAFYCallbackPage.jsx
@@ -2,8 +2,9 @@ import { useEffect, useState, useRef } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../context/useAuth';
import { ssafyCallback } from '../api/auth';
+import './AuthPage.css';
-// Inline styles are intentional for this simple callback/loading page — not worth a separate CSS file.
+// Inline styles are intentional for this simple callback/loading page — layout only, AuthPage.css handles buttons.
export default function SSAFYCallbackPage() {
const [error, setError] = useState('');
const navigate = useNavigate();
@@ -39,7 +40,7 @@ export default function SSAFYCallbackPage() {
return (
{error}
-
diff --git a/src/pages/WalletPage.css b/src/pages/WalletPage.css
index 22c06bd..74771a3 100644
--- a/src/pages/WalletPage.css
+++ b/src/pages/WalletPage.css
@@ -367,51 +367,6 @@
background: rgba(186, 205, 176, 0.08);
}
-/* Wallet summary card (HomePage) */
-.wallet-summary {
- background: rgba(186, 205, 176, 0.06);
- border: 1px solid rgba(186, 205, 176, 0.15);
- border-radius: 10px;
- padding: 20px 24px;
- margin-bottom: 32px;
- cursor: pointer;
- transition: border-color 0.2s, background 0.2s;
-}
-
-.wallet-summary:hover {
- border-color: rgba(186, 205, 176, 0.3);
- background: rgba(186, 205, 176, 0.09);
-}
-
-.wallet-summary-title {
- font-size: 0.75rem;
- text-transform: uppercase;
- color: rgba(255, 255, 255, 0.4);
- letter-spacing: 0.08em;
- margin: 0 0 12px;
-}
-
-.wallet-summary-balance {
- font-size: 1.8rem;
- font-weight: 700;
- color: #BACDB0;
- margin: 0;
-}
-
-.wallet-summary-stats {
- display: flex;
- gap: 24px;
- margin-top: 12px;
-}
-
-.wallet-summary-stat {
- font-size: 0.85rem;
- color: rgba(255, 255, 255, 0.5);
-}
-
-.wallet-summary-stat strong {
- color: rgba(255, 255, 255, 0.8);
-}
.wallet-logo {
height: 32px;
@@ -470,11 +425,6 @@
align-items: stretch;
}
- .wallet-summary-stats {
- flex-direction: column;
- gap: 8px;
- }
-
.market-item {
flex-direction: column;
align-items: stretch;