Skip to content

Commit cfd86f9

Browse files
authored
Merge pull request #248 from ItRecode/FE-242
[FE-242] feat: 피드백 이메일 api 연동
2 parents 62ee2da + 30e4513 commit cfd86f9

4 files changed

Lines changed: 158 additions & 63 deletions

File tree

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"private": true,
55
"dependencies": {
66
"@craco/craco": "^7.0.0",
7+
"@emailjs/browser": "^3.10.0",
78
"@tanstack/react-query": "^4.20.4",
89
"@tanstack/react-query-devtools": "^4.20.4",
910
"@testing-library/jest-dom": "^5.16.5",

src/components/Toast.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@ interface IToastProps {
66
message: ReactNode
77
timeLimit?: number
88
onClose: () => void
9+
hasSecondMessage?: boolean
10+
size?: 'big' | 'basic'
911
}
1012

11-
function Toast({ visible, message, timeLimit = 2, onClose }: IToastProps) {
13+
function Toast({
14+
visible,
15+
message,
16+
timeLimit = 2,
17+
onClose,
18+
hasSecondMessage = true,
19+
size = 'basic',
20+
}: IToastProps) {
1221
const [times, setTimes] = useState(timeLimit)
1322
const interval: { current: NodeJS.Timeout | undefined } = useRef()
1423
useEffect(() => {
@@ -24,13 +33,19 @@ function Toast({ visible, message, timeLimit = 2, onClose }: IToastProps) {
2433

2534
return (
2635
<Modal visible={visible} onClose={onClose}>
27-
<div className="flex h-[124px] w-[270px] flex-col justify-center px-12 py-6">
28-
<p className="mb-4 text-center font-semibold leading-normal text-grey-10">
36+
<div
37+
className={`flex ${
38+
size === 'basic' ? 'h-[124px]' : 'h-[148px]'
39+
} w-[270px] flex-col justify-center px-12 py-6`}
40+
>
41+
<div className="mb-4 text-center font-semibold leading-normal text-grey-10">
2942
{message}
30-
</p>
31-
<p className="text-center text-xs font-medium text-grey-8">
32-
<span className="text-primary-2">{times}</span>초 뒤에 사라집니다.
33-
</p>
43+
</div>
44+
{hasSecondMessage && (
45+
<p className="text-center text-xs font-medium text-grey-8">
46+
<span className="text-primary-2">{times}</span>초 뒤에 사라집니다.
47+
</p>
48+
)}
3449
</div>
3550
</Modal>
3651
)

src/pages/Setting/FeedbackMail/FeedbackMail.tsx

Lines changed: 121 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,135 @@
11
import BackButton from '@components/BackButton'
22
import Button from '@components/Button'
3-
import React, { useState } from 'react'
3+
import React, { useRef, useState } from 'react'
4+
import emailjs from '@emailjs/browser'
5+
import Toast from '@components/Toast'
46

57
function FeedbackMail() {
8+
const [isOpenToast, setIsOpenToast] = useState(false)
69
const [subject, setSubject] = useState('')
710
const [body, setBody] = useState('')
811

12+
const formRef = useRef<HTMLFormElement>(null)
13+
14+
const sendEmail = (e: React.FormEvent<HTMLFormElement>) => {
15+
e.preventDefault()
16+
17+
const {
18+
REACT_APP_EMAILJS_SERVICE_ID,
19+
REACT_APP_EMAILJS_TEMPLATE_ID,
20+
REACT_APP_EMAILJS_PUBLIC_KEY,
21+
} = process.env
22+
23+
if (formRef.current !== null) {
24+
emailjs
25+
.sendForm(
26+
REACT_APP_EMAILJS_SERVICE_ID as string,
27+
REACT_APP_EMAILJS_TEMPLATE_ID as string,
28+
e.currentTarget,
29+
REACT_APP_EMAILJS_PUBLIC_KEY as string
30+
)
31+
.then(
32+
() => {
33+
setIsOpenToast(true)
34+
setSubject('')
35+
setBody('')
36+
},
37+
(error) => {
38+
throw error
39+
}
40+
)
41+
}
42+
}
943
return (
1044
<>
11-
<div className="px-6">
12-
<header className="pt-4">
13-
<BackButton />
14-
</header>
45+
{isOpenToast && (
46+
<Toast
47+
visible={true}
48+
timeLimit={2}
49+
message={
50+
<>
51+
<div className="mt-6 font-semibold">
52+
<p>
53+
<span className="text-primary-2">피드백 전송</span>
54+
</p>
55+
<p>완료되었어요</p>
56+
</div>
57+
<div className="mt-4 text-xs font-medium leading-normal text-grey-8">
58+
<p>더 편리한 이용을 하실 수 있도록</p>
59+
<p>빠르게 개선할게요!</p>
60+
</div>
61+
</>
62+
}
63+
onClose={() => setIsOpenToast(false)}
64+
hasSecondMessage={false}
65+
size="big"
66+
/>
67+
)}
68+
<form ref={formRef} onSubmit={(e) => sendEmail(e)}>
69+
<div className="px-6">
70+
<header className="pt-4">
71+
<BackButton />
72+
</header>
1573

16-
<section id="notice" className="mt-10">
17-
<div className="text-lg font-semibold">
18-
<p>여러분의 소리를 듣기 위해</p>
19-
<p>레코딧은 항상 열려있어요!</p>
20-
<p>
21-
<span className="text-primary-2">자유롭게 피드백</span>
22-
보내주세요!
74+
<section id="notice" className="mt-10">
75+
<div className="text-lg font-semibold">
76+
<p>여러분의 소리를 듣기 위해</p>
77+
<p>레코딧은 항상 열려있어요!</p>
78+
<p>
79+
<span className="text-primary-2">자유롭게 피드백</span>
80+
보내주세요!
81+
</p>
82+
</div>
83+
<div className="mt-4 text-xs leading-normal text-grey-7">
84+
<p>레코딧을 사용하며 겪은 불편함이 있으신가요?</p>
85+
<p>또는 텐져스에게 전달하고 싶은 내용이 있으신가요?</p>
86+
<p>남겨주시는 내용은 한글자도 빼놓지 않고 꼼꼼히 살펴볼게요.</p>
87+
</div>
88+
</section>
89+
90+
<section id="title" className="mt-10">
91+
<p className="text-xs font-semibold text-primary-2">
92+
레코딧 피드백 제목
93+
</p>
94+
<div className="mt-4 flex items-center">
95+
<input
96+
name="subject"
97+
type="text"
98+
className="w-full border-b border-solid border-grey-4 py-1.5 text-grey-9 outline-none placeholder:text-grey-4 focus:placeholder:text-transparent"
99+
placeholder="제목을 작성해주세요."
100+
onChange={(e) => setSubject(e.target.value)}
101+
value={subject}
102+
/>
103+
<span className="absolute right-6 text-xs text-grey-4">
104+
{subject.length}/20
105+
</span>
106+
</div>
107+
</section>
108+
<section id="body" className="mt-[57px]">
109+
<p className="text-xs font-semibold text-primary-2">
110+
레코딧 피드백 내용
23111
</p>
24-
</div>
25-
<div className="mt-4 text-xs leading-normal text-grey-7">
26-
<p>레코딧을 사용하며 겪은 불편함이 있으신가요?</p>
27-
<p>또는 텐져스에게 전달하고 싶은 내용이 있으신가요?</p>
28-
<p>남겨주시는 내용은 한글자도 빼놓지 않고 꼼꼼히 살펴볼게요.</p>
29-
</div>
30-
</section>
31-
<section id="title" className="mt-10">
32-
<p className="text-xs font-semibold text-primary-2">
33-
레코딧 피드백 제목
34-
</p>
35-
<div className="mt-4 flex items-center">
36-
<input
37-
className="w-full border-b border-solid border-grey-4 py-1.5 text-grey-9 outline-none placeholder:text-grey-4 focus:placeholder:text-transparent"
38-
placeholder="제목을 작성해주세요."
39-
onChange={(e) => setSubject(e.target.value)}
40-
value={subject}
41-
/>
42-
<span className="absolute right-6 text-xs text-grey-4">
43-
{subject.length}/20
44-
</span>
45-
</div>
46-
</section>
47-
<section id="body" className="mt-[57px]">
48-
<p className="text-xs font-semibold text-primary-2">
49-
레코딧 피드백 내용
50-
</p>
51-
<div className="relative flex items-end justify-end">
52-
<textarea
53-
className="mt-4 h-[130px] w-full resize-none rounded-lg bg-grey-2 p-4 leading-normal text-grey-9 outline-0 placeholder:text-grey-5 focus:placeholder:text-transparent"
54-
placeholder="피드백 내용을 자유롭게 작성해주세요."
55-
maxLength={200}
56-
onChange={(e) => setBody(e.target.value)}
57-
value={body}
58-
/>
59-
<span className="absolute bottom-2 right-4 text-xs font-normal leading-none text-grey-4">
60-
{body.length}/200
61-
</span>
62-
</div>
63-
</section>
64-
</div>
65-
<div className="absolute bottom-10 w-full cursor-pointer px-6">
66-
<Button property="solid">피드백 보내기</Button>
67-
</div>
112+
<div className="relative flex items-end justify-end">
113+
<textarea
114+
name="body"
115+
className="mt-4 h-[130px] w-full resize-none rounded-lg bg-grey-2 p-4 leading-normal text-grey-9 outline-0 placeholder:text-grey-5 focus:placeholder:text-transparent"
116+
placeholder="피드백 내용을 자유롭게 작성해주세요."
117+
maxLength={200}
118+
onChange={(e) => setBody(e.target.value)}
119+
value={body}
120+
/>
121+
<span className="absolute bottom-2 right-4 text-xs font-normal leading-none text-grey-4">
122+
{body.length}/200
123+
</span>
124+
</div>
125+
</section>
126+
</div>
127+
<div className="absolute bottom-10 w-full cursor-pointer px-6">
128+
<Button property="solid" type="submit">
129+
피드백 보내기
130+
</Button>
131+
</div>
132+
</form>
68133
</>
69134
)
70135
}

0 commit comments

Comments
 (0)