Skip to content

Commit a6cb400

Browse files
authored
feature(graduate): Client 사이드 기능 API 연동 (#220)
2 parents 89516d3 + e4fb431 commit a6cb400

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+63332
-476
lines changed

apps/graduate/public/pdf.worker.min.js

Lines changed: 61681 additions & 1 deletion
Large diffs are not rendered by default.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useMutation } from '@tanstack/react-query';
2+
3+
import { patch } from '~/shared/api';
4+
import { END_POINT } from '~/shared/constants';
5+
6+
const submitConfirmEmail = async (email: string) => {
7+
const response = await patch({
8+
request: END_POINT.USER.CONFIRM_EMAIL,
9+
data: { email },
10+
});
11+
return response.data;
12+
};
13+
14+
export const useSubmitConfirmEmail = () => {
15+
return useMutation({
16+
mutationFn: submitConfirmEmail,
17+
});
18+
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { style } from '@vanilla-extract/css';
2+
3+
import { vars } from '~/vars.css';
4+
5+
export const container = style({
6+
display: 'grid',
7+
placeItems: 'center',
8+
width: '100%',
9+
maxWidth: '768px',
10+
margin: 'auto',
11+
gap: vars.spacing.xl,
12+
paddingRight: vars.spacing.lg,
13+
paddingLeft: vars.spacing.lg,
14+
boxSizing: 'border-box',
15+
});
16+
17+
export const wrapper = style({
18+
display: 'flex',
19+
flexDirection: 'column',
20+
alignItems: 'center',
21+
justifyContent: 'center',
22+
});
23+
24+
export const iconWrapper = style({
25+
width: '80px',
26+
height: '80px',
27+
backgroundColor: '#E6F0FF',
28+
borderRadius: '50%',
29+
display: 'flex',
30+
alignItems: 'center',
31+
justifyContent: 'center',
32+
color: '#006AE4',
33+
marginBottom: '24px',
34+
});
35+
36+
export const title = style({
37+
fontSize: '24px',
38+
fontVariationSettings: `'wght' ${vars.font.weight.semibold}`,
39+
color: '#111827',
40+
marginBottom: '12px',
41+
letterSpacing: '-0.02em',
42+
});
43+
44+
export const description = style({
45+
fontSize: '16px',
46+
color: '#6B7280',
47+
lineHeight: '1.6',
48+
marginBottom: '32px',
49+
});
50+
51+
export const inputSection = style({
52+
width: '100%',
53+
display: 'flex',
54+
flexDirection: 'column',
55+
gap: '8px',
56+
marginBottom: '32px',
57+
textAlign: 'left',
58+
});
59+
60+
export const label = style({
61+
fontSize: '14px',
62+
fontWeight: 600,
63+
color: '#374151',
64+
marginLeft: '4px',
65+
});
66+
67+
export const input = style({
68+
width: '100%',
69+
height: '52px',
70+
padding: '0 16px',
71+
borderRadius: '12px',
72+
border: '1px solid #E5E7EB',
73+
fontSize: '15px',
74+
backgroundColor: '#F9FAFB',
75+
transition: 'all 0.2s ease',
76+
boxSizing: 'border-box',
77+
78+
':focus': {
79+
outline: 'none',
80+
borderColor: '#006AE4',
81+
backgroundColor: '#FFFFFF',
82+
},
83+
});
84+
85+
export const button = style({
86+
width: '100%',
87+
height: '56px',
88+
backgroundColor: '#006AE4',
89+
color: '#FFFFFF',
90+
borderRadius: '16px',
91+
fontSize: '16px',
92+
fontWeight: 700,
93+
border: 'none',
94+
cursor: 'pointer',
95+
transition: 'all 0.2s ease',
96+
97+
':hover': {
98+
backgroundColor: '#0054B3',
99+
transform: 'translateY(-2px)',
100+
boxShadow: '0 6px 20px rgba(0, 106, 228, 0.3)',
101+
},
102+
':active': {
103+
transform: 'translateY(0)',
104+
},
105+
});

apps/graduate/src/pages/client/apply/styles/ApplyPage.css.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,64 @@ import { style } from '@vanilla-extract/css';
33
import { vars } from '~/vars.css';
44

55
export const container = style({
6-
display: 'grid',
7-
placeItems: 'center',
6+
display: 'flex',
7+
flexDirection: 'column',
88
width: '100%',
9-
maxWidth: '768px',
10-
margin: 'auto',
9+
maxWidth: '800px',
10+
margin: '0 auto',
1111
gap: vars.spacing.xl,
12+
padding: vars.spacing.xl,
13+
backgroundColor: vars.colors.white,
14+
borderRadius: vars.radius.xl,
15+
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.06)',
16+
border: `1px solid ${vars.colors.subHover}`,
17+
boxSizing: 'border-box',
1218
});
1319

1420
export const button = style({
1521
width: '100%',
16-
padding: vars.spacing.lg,
22+
height: '56px',
1723
borderRadius: vars.radius.lg,
24+
fontSize: vars.font.size.lg,
25+
fontWeight: 700,
26+
marginTop: vars.spacing.lg,
1827
});
1928

2029
export const optionButton = style({
2130
width: '100%',
22-
padding: vars.spacing.lg,
31+
height: '100px',
32+
display: 'flex',
33+
flexDirection: 'column',
34+
alignItems: 'center',
35+
justifyContent: 'center',
2336
borderRadius: vars.radius.lg,
2437
backgroundColor: vars.colors.white,
25-
border: `1px solid ${vars.colors.border}`,
38+
border: `1px solid ${vars.colors.subHover}`,
2639
color: vars.colors.subDark,
27-
fontVariationSettings: `'wght' ${vars.font.weight.medium}`,
40+
transition: 'all 0.2s ease',
41+
fontWeight: 500,
42+
cursor: 'pointer',
2843
':hover': {
2944
backgroundColor: vars.colors.sub,
45+
transform: 'translateY(-2px)',
3046
},
3147
});
3248

3349
export const activeOptionButton = style({
3450
width: '100%',
35-
padding: vars.spacing.lg,
51+
height: '100px',
52+
display: 'flex',
53+
flexDirection: 'column',
54+
alignItems: 'center',
55+
justifyContent: 'center',
3656
borderRadius: vars.radius.lg,
3757
backgroundColor: vars.colors.mainXLight,
38-
border: `1px solid ${vars.colors.main}`,
39-
color: vars.colors.subText,
40-
fontVariationSettings: `'wght' ${vars.font.weight.medium}`,
41-
':hover': {
42-
backgroundColor: vars.colors.sub,
43-
},
58+
border: `2px solid ${vars.colors.main}`,
59+
color: vars.colors.main,
60+
transition: 'all 0.2s ease',
61+
fontWeight: 700,
62+
boxShadow: '0 4px 12px rgba(0, 106, 228, 0.1)',
63+
cursor: 'pointer',
4464
});
4565

4666
export const drawer = style({
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { useNavigate } from '@tanstack/react-router';
2+
import { message } from 'antd';
3+
import { Check } from 'lucide-react';
4+
import { useState } from 'react';
5+
6+
import { ROUTE } from '~/shared/constants';
7+
8+
import { useSubmitConfirmEmail } from '../api/submitConfirmEmail';
9+
import * as styles from '../styles/ApplyConfirmPage.css';
10+
11+
export default function ApplyConfirmPage() {
12+
const navigate = useNavigate();
13+
const [email, setEmail] = useState('');
14+
const { mutate: submitConfirmEmail, isPending: isSubmitting } =
15+
useSubmitConfirmEmail();
16+
17+
const handleComplete = () => {
18+
if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
19+
message.error('올바른 이메일 형식을 입력해주세요.');
20+
return;
21+
}
22+
23+
if (email) {
24+
submitConfirmEmail(email, {
25+
onSuccess: () => {
26+
message.success(`${email}로 알림 설정을 완료했습니다.`);
27+
navigate({ to: ROUTE.HOME });
28+
},
29+
onError: () => {
30+
message.error('이메일 등록에 실패했습니다. 다시 시도해주세요.');
31+
},
32+
});
33+
} else {
34+
message.success('신청이 완료되었습니다.');
35+
navigate({ to: ROUTE.HOME });
36+
}
37+
};
38+
39+
return (
40+
<div className={styles.container}>
41+
<div className={styles.wrapper}>
42+
<div className={styles.iconWrapper}>
43+
<Check size={40} strokeWidth={3} />
44+
</div>
45+
46+
<h1 className={styles.title}>신청이 완료되었습니다</h1>
47+
<p className={styles.description}>
48+
졸업 요건 취득 방식 신청이 정상적으로 접수되었습니다.
49+
<br />
50+
학사 승인까지 영업일 기준 3~5일이 소요될 수 있습니다.
51+
</p>
52+
53+
<div className={styles.inputSection}>
54+
<label htmlFor='email' className={styles.label}>
55+
알림 받을 이메일 (선택)
56+
</label>
57+
<input
58+
id='email'
59+
type='email'
60+
placeholder='example@kgu.ac.kr'
61+
className={styles.input}
62+
value={email}
63+
onChange={e => setEmail(e.target.value)}
64+
disabled={isSubmitting}
65+
/>
66+
</div>
67+
68+
<button
69+
className={styles.button}
70+
onClick={handleComplete}
71+
disabled={isSubmitting}
72+
>
73+
{isSubmitting ? '처리 중...' : '홈으로 돌아가기'}
74+
</button>
75+
</div>
76+
</div>
77+
);
78+
}

apps/graduate/src/pages/client/apply/ui/ApplyDrawer.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Link, useRouter } from '@tanstack/react-router';
1+
import { useRouter } from '@tanstack/react-router';
22
import { Button, Drawer, Steps } from 'antd';
33

44
import { type GraduationType, ROUTE } from '~/shared/constants';
@@ -48,17 +48,15 @@ export default function ApplyDrawer({
4848
className={styles.drawerDescription}
4949
/>
5050

51-
<Link to={ROUTE.HOME}>
52-
<Button
53-
onClick={submitGraduationType}
54-
loading={isSubmitting}
55-
size='large'
56-
className={styles.submitButton}
57-
type='primary'
58-
>
59-
제출하기
60-
</Button>
61-
</Link>
51+
<Button
52+
onClick={submitGraduationType}
53+
loading={isSubmitting}
54+
size='large'
55+
className={styles.submitButton}
56+
type='primary'
57+
>
58+
제출하기
59+
</Button>
6260
</Drawer>
6361
);
6462
}

0 commit comments

Comments
 (0)