Skip to content

Commit e7d506f

Browse files
authored
Merge pull request #205 from CSE-Shaco/develop
fix(manito): UI/UX 개선에 따른 변경사항 반영
2 parents 10e55b6 + b5de487 commit e7d506f

File tree

1 file changed

+104
-63
lines changed

1 file changed

+104
-63
lines changed

src/app/manitto/page.jsx

Lines changed: 104 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export default function ManitoVerifyPage() {
1717
const [loading, setLoading] = useState(false);
1818
const [error, setError] = useState('');
1919
const [cipher, setCipher] = useState('');
20-
const [plain, setPlain] = useState(null); // 복호화 결과(JSON 등)
20+
const [plain, setPlain] = useState(null); // { receiverStudentId, receiverName }
21+
const [ownerName, setOwnerName] = useState(''); // 요청자 이름 (API에서 내려주는 값 가정)
2122

2223
useEffect(() => {
2324
if (!searchParams) return;
@@ -36,6 +37,7 @@ export default function ManitoVerifyPage() {
3637
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
3738
while (base64.length % 4 !== 0) base64 += '=';
3839
const binary = typeof atob !== 'undefined' ? atob(base64) : Buffer.from(base64, 'base64').toString('binary');
40+
3941
const bytes = new Uint8Array(binary.length);
4042
for (let i = 0; i < binary.length; i++) {
4143
bytes[i] = binary.charCodeAt(i);
@@ -93,6 +95,7 @@ export default function ManitoVerifyPage() {
9395
if (decoded) {
9496
setPlain(decoded);
9597
} else {
98+
// 여기서도 error 세팅
9699
setError((prev) => (prev ? prev + '\n' : '') + '복호화에 실패했습니다. 해시 값이 올바른지 확인해 주세요.',);
97100
setPlain(null);
98101
}
@@ -108,6 +111,7 @@ export default function ManitoVerifyPage() {
108111
setError('');
109112
setCipher('');
110113
setPlain(null);
114+
setOwnerName('');
111115

112116
if (!sessionCode || !studentId) {
113117
setError('세션 코드 또는 학번 정보가 잘못되었습니다. 링크를 다시 확인해 주세요.');
@@ -126,10 +130,12 @@ export default function ManitoVerifyPage() {
126130

127131
const body = res.data;
128132

129-
// ✅ 서버 응답 키 이름에 맞게 수정 (encryptedManito)
133+
// ✅ 서버 응답 키 이름 맞춰서 사용
130134
const encrypted = body?.data?.encryptedManito || '';
135+
const owner = body?.data?.ownerName || ''; // 백엔드에서 내려준다고 가정
131136

132-
setCipher(encrypted); // 🔥 cipher가 바뀌면 위 useEffect가 hash로 복호화
137+
setOwnerName(owner);
138+
setCipher(encrypted); // 🔥 cipher가 바뀌면 useEffect가 hash로 복호화
133139
} catch (err) {
134140
const res = err?.response;
135141
const msg = res?.data?.message || res?.data?.error || err?.message || '알 수 없는 오류가 발생했습니다.';
@@ -141,66 +147,101 @@ export default function ManitoVerifyPage() {
141147

142148
const disabled = !sessionCode || !studentId;
143149

144-
return (<div className="dark min-h-[100svh] flex items-center justify-center bg-black px-4">
145-
<Card className="max-w-md w-full bg-zinc-900 border border-zinc-800">
146-
<CardHeader className="flex flex-col items-start gap-2">
147-
<h1 className="text-2xl font-bold text-white">마니또 확인</h1>
148-
<p className="text-sm text-zinc-400">
149-
전달받은 링크로 접속한 뒤, 본인이 설정한 PIN 4자리를 입력해 주세요.
150-
</p>
151-
</CardHeader>
152-
<Divider className="border-zinc-800"/>
153-
<CardBody className="flex flex-col gap-4 text-white">
154-
{disabled && (<p className="text-xs text-red-400">
155-
필수 정보가 누락되었습니다. 링크를 다시 확인해 주세요.
156-
</p>)}
157-
158-
<form onSubmit={handleSubmit} className="flex flex-col gap-4 mt-2">
159-
<Input
160-
label="PIN (숫자 4자리)"
161-
type="password"
162-
variant="bordered"
163-
value={pin}
164-
maxLength={4}
165-
onChange={(e) => setPin(e.target.value.replace(/[^0-9]/g, '').slice(0, 4))}
166-
classNames={{
167-
label: 'text-zinc-300',
168-
input: 'text-white',
169-
inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-400',
170-
}}
171-
isDisabled={disabled || loading}
172-
/>
173-
174-
{error && (<p className="text-xs text-red-400 whitespace-pre-line">
175-
{error}
176-
</p>)}
177-
178-
<Button
179-
type="submit"
180-
color="primary"
181-
isLoading={loading}
182-
isDisabled={disabled || loading || !pin}
183-
className="font-semibold"
184-
>
150+
// 결과 문구 구성
151+
const receiverName = plain?.receiverName;
152+
const ownerLabel = ownerName || '당신';
153+
154+
return (<div
155+
className="dark min-h-[100svh] flex items-center justify-center bg-gradient-to-br from-zinc-950 via-black to-zinc-900 px-4"
156+
>
157+
<Card className="max-w-md w-full bg-zinc-900/90 border border-zinc-800 shadow-2xl">
158+
<CardHeader className="flex flex-col gap-2 items-start">
159+
<span className="text-xs text-zinc-400">🎁 GDGoC INHA · 마니또</span>
160+
<h1 className="text-2xl font-bold text-white">
185161
마니또 확인하기
186-
</Button>
187-
</form>
188-
189-
{/* 결과 영역 */}
190-
{(cipher || plain) && (<>
191-
<Divider className="border-zinc-800 my-2"/>
192-
<div className="space-y-2 text-sm">
193-
<p className="font-semibold text-zinc-200">결과</p>
194-
195-
{plain ? (<>
196-
<pre className="text-xs bg-zinc-950 border border-zinc-800 rounded-lg p-3 overflow-x-auto">
197-
{JSON.stringify(plain, null, 2)}
198-
</pre>
199-
</>) : (<>
162+
</h1>
163+
<p className="text-xs text-zinc-400">
164+
전달받은 링크로 접속한 뒤,<br/>
165+
본인이 설정한 PIN 4자리를 입력해 주세요.
166+
</p>
167+
</CardHeader>
168+
169+
<Divider className="border-zinc-800"/>
170+
171+
<CardBody className="flex flex-col gap-4 text-white">
172+
{disabled && (<p className="text-xs text-red-400">
173+
필수 정보가 누락되었습니다. 링크를 다시 확인해 주세요.
174+
</p>)}
175+
176+
<form onSubmit={handleSubmit} className="flex flex-col gap-4 mt-1">
177+
<Input
178+
label="PIN (숫자 4자리)"
179+
type="password"
180+
variant="bordered"
181+
value={pin}
182+
maxLength={4}
183+
placeholder="ex) 0420"
184+
onChange={(e) => setPin(e.target.value.replace(/[^0-9]/g, '').slice(0, 4),)}
185+
classNames={{
186+
label: 'text-zinc-300',
187+
input: 'text-white text-base tracking-[0.25em]',
188+
inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-300',
189+
}}
190+
isDisabled={disabled || loading}
191+
/>
192+
193+
{error && (<p className="text-xs text-red-400 whitespace-pre-line">
194+
{error}
195+
</p>)}
196+
197+
<Button
198+
type="submit"
199+
color="primary"
200+
isLoading={loading}
201+
isDisabled={disabled || loading || !pin}
202+
className="font-semibold bg-gradient-to-r from-sky-500 to-cyan-400 text-black"
203+
>
204+
마니또 확인하기 ✨
205+
</Button>
206+
</form>
207+
208+
{/* 결과 영역 */}
209+
{(cipher || plain) && (<>
210+
<Divider className="border-zinc-800 my-2"/>
211+
<div className="space-y-3 text-sm">
212+
<p className="text-xs text-zinc-400">
213+
결과
214+
</p>
215+
216+
{plain ? (<div
217+
className="rounded-lg bg-zinc-950/80 border border-zinc-800 px-4 py-3 space-y-2"
218+
>
219+
<p className="text-base font-semibold">
220+
{ownerLabel}
221+
<span className="text-zinc-300">의 마니또는 </span>
222+
<span className="text-sky-400">
223+
{receiverName || '알 수 없음'}
224+
</span>
225+
<span className="text-zinc-300"> 님입니다! 🎉</span>
226+
</p>
227+
<p className="text-[11px] text-zinc-500 mt-2">
228+
이 정보는 브라우저에서 해시값을 사용해 복호화한 결과이며,
229+
서버에는 평문으로 저장되지 않습니다.
230+
</p>
231+
</div>) : (<div
232+
className="rounded-lg bg-zinc-950/80 border border-red-500/40 px-4 py-3 space-y-1"
233+
>
234+
<p className="text-sm font-semibold text-red-400">
235+
복호화에 실패하였습니다.
236+
</p>
237+
<p className="text-[11px] text-zinc-500">
238+
해시 값이 올바른지, 전달받은 링크가 정확한지 다시 한 번
239+
확인해 주세요. 문제가 계속되면 운영진에게 문의해 주세요.
240+
</p>
241+
</div>)}
242+
</div>
200243
</>)}
201-
</div>
202-
</>)}
203-
</CardBody>
204-
</Card>
205-
</div>);
244+
</CardBody>
245+
</Card>
246+
</div>);
206247
}

0 commit comments

Comments
 (0)