Skip to content

Commit d8f913c

Browse files
committed
feat: contact
1 parent 34ae6da commit d8f913c

5 files changed

Lines changed: 214 additions & 1 deletion

File tree

src/pages/contact/index.jsx

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import React, { useRef, useState } from 'react'
2+
import FixTabPanel from '@stateless/FixTabPanel'
3+
import { Send, MapPin, Mail } from 'lucide-react'
4+
5+
const Contact = () => {
6+
const scrollRef = useRef(null)
7+
const [formData, setFormData] = useState({
8+
name: '',
9+
email: '',
10+
subject: '',
11+
message: '',
12+
})
13+
14+
const [errors, setErrors] = useState({})
15+
const [status, setStatus] = useState(null)
16+
const validateForm = () => {
17+
let tempErrors = {}
18+
let isValid = true
19+
20+
if (!formData.name.trim()) {
21+
tempErrors.name = 'Name is required'
22+
isValid = false
23+
}
24+
25+
if (!formData.email.trim()) {
26+
tempErrors.email = 'Email is required'
27+
isValid = false
28+
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
29+
tempErrors.email = 'Email is invalid'
30+
isValid = false
31+
}
32+
33+
if (!formData.subject.trim()) {
34+
tempErrors.subject = 'Subject is required'
35+
isValid = false
36+
}
37+
38+
if (!formData.message.trim()) {
39+
tempErrors.message = 'Message is required'
40+
isValid = false
41+
}
42+
43+
setErrors(tempErrors)
44+
return isValid
45+
}
46+
47+
const handleSubmit = async (e) => {
48+
e.preventDefault()
49+
50+
if (!validateForm()) {
51+
setStatus('Please fill in all required fields correctly.')
52+
return
53+
}
54+
55+
// Create a new FormData object to send to Web3Forms API
56+
const form = new FormData()
57+
form.append('access_key', '44b28ec2-e0a1-459a-8e3b-06869186889b')
58+
form.append('name', formData.name)
59+
form.append('email', formData.email)
60+
form.append('subject', formData.subject || 'New Contact Form Submission')
61+
form.append('message', formData.message)
62+
63+
try {
64+
const response = await fetch('https://api.web3forms.com/submit', {
65+
method: 'POST',
66+
body: form,
67+
})
68+
69+
const result = await response.json()
70+
71+
if (response.ok) {
72+
setStatus('Message sent successfully!')
73+
setFormData({
74+
name: '',
75+
email: '',
76+
subject: '',
77+
message: '',
78+
})
79+
setErrors({})
80+
} else {
81+
setStatus(result.message || 'There was an error sending your message.')
82+
}
83+
} catch (error) {
84+
setStatus('An error occurred. Please try again.')
85+
console.error('Error:', error)
86+
}
87+
}
88+
const handleInputChange = (e) => {
89+
const { name, value } = e.target
90+
setFormData((prev) => ({
91+
...prev,
92+
[name]: value,
93+
}))
94+
}
95+
return (
96+
<FixTabPanel>
97+
<div className="code-window relative h-full w-[50%]">
98+
<h2 className="mb-4 bg-gradient-to-r from-blue-400 to-purple-600 bg-clip-text text-5xl font-bold text-transparent">
99+
Get in Touch
100+
</h2>
101+
<form onSubmit={handleSubmit} className="space-y-6">
102+
<div className="grid grid-cols-1 gap-6">
103+
<div>
104+
<input
105+
type="text"
106+
name="name"
107+
placeholder="Your Name"
108+
className={`w-full rounded-xl border px-4 py-3 ${
109+
errors.name ? 'border-red-500' : 'border-gray-300/50 bg-white/30'
110+
} backdrop-blur-sm transition-colors focus:border-blue-500 focus:outline-none`}
111+
value={formData.name}
112+
onChange={handleInputChange}
113+
/>
114+
{errors.name && <p className="mt-1 text-sm text-red-500">{errors.name}</p>}
115+
</div>
116+
117+
<div>
118+
<input
119+
type="email"
120+
name="email"
121+
placeholder="Your Email"
122+
className={`w-full rounded-xl border px-4 py-3 ${
123+
errors.email ? 'border-red-500' : 'border-gray-300/50 bg-white/30 text-gray-800'
124+
} backdrop-blur-sm transition-colors focus:border-blue-500 focus:outline-none`}
125+
value={formData.email}
126+
onChange={handleInputChange}
127+
/>
128+
{errors.email && <p className="mt-1 text-sm text-red-500">{errors.email}</p>}
129+
</div>
130+
131+
<div>
132+
<input
133+
type="text"
134+
name="subject"
135+
placeholder="Subject"
136+
className={`w-full rounded-xl border px-4 py-3 ${
137+
errors.subject ? 'border-red-500' : 'border-gray-300/50 bg-white/30 text-gray-800'
138+
} backdrop-blur-sm transition-colors focus:border-blue-500 focus:outline-none`}
139+
value={formData.subject}
140+
onChange={handleInputChange}
141+
/>
142+
{errors.subject && <p className="mt-1 text-sm text-red-500">{errors.subject}</p>}
143+
</div>
144+
145+
<div>
146+
<textarea
147+
name="message"
148+
placeholder="Your Message"
149+
rows={4}
150+
className={`w-full resize-none rounded-xl border px-4 py-3 ${
151+
errors.message ? 'border-red-500' : 'border-gray-300/50 bg-white/30 text-gray-800'
152+
} backdrop-blur-sm transition-colors focus:border-blue-500 focus:outline-none`}
153+
value={formData.message}
154+
onChange={handleInputChange}
155+
></textarea>
156+
{errors.message && <p className="mt-1 text-sm text-red-500">{errors.message}</p>}
157+
</div>
158+
</div>
159+
160+
<button
161+
type="submit"
162+
className="flex w-full items-center justify-center space-x-2 rounded-xl bg-gradient-to-r from-blue-500 to-purple-600 px-6 py-3 font-semibold text-white backdrop-blur-sm transition-all duration-300 hover:from-blue-600 hover:to-purple-700 hover:shadow-lg"
163+
>
164+
<span>Send Message</span>
165+
<Send className="h-4 w-4" />
166+
</button>
167+
</form>
168+
169+
{status && (
170+
<div
171+
className={`mt-4 rounded-lg p-3 backdrop-blur-sm ${
172+
status.includes('success')
173+
? 'border border-green-500/20 bg-green-500/10 text-green-400'
174+
: 'border border-red-500/20 bg-red-500/10 text-red-400'
175+
}`}
176+
>
177+
<p className="text-center">{status}</p>
178+
</div>
179+
)}
180+
</div>
181+
</FixTabPanel>
182+
)
183+
}
184+
185+
export default Contact
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.profile {
2+
display: flex;
3+
}

src/pages/layout/proHeader/index.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ const ProHeader = () => {
5353
},
5454
{
5555
key: '3',
56+
label: <Space>联 系 我</Space>,
57+
icon: <SmileOutlined />,
58+
onClick: () => {
59+
redirectTo('/contact')
60+
},
61+
},
62+
{
63+
key: '4',
5664
label: <Space>退出登录</Space>,
5765
icon: <LogoutOutlined />,
5866
onClick: () => {

src/pages/profile/index.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
44
import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism'
55

66
const codeString = `const profile = {
7-
name: 'wkylin.w',
7+
name: 'Wkylin Wang',
88
title: 'Full-Stack Developer',
99
skills: [
1010
'React', 'Vue' 'NextJS', 'Redux', 'TypeScript',
1111
'MySQL', 'MongoDB', 'Docker', 'Express',
1212
'GraphQL', 'Git',
1313
],
14+
hobbies: ['Music', 'Travel', 'Film', 'Reading'],
15+
languages: ['English', 'Chinese'],
16+
vision: {
17+
life: 'Colourful',
18+
work: 'Successful',
19+
love: 'Sweet',
20+
family: 'Healthy',
21+
},
1422
hardWorker: true,
1523
quickLearner: true,
1624
problemSolver: true,

src/routers/index.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const NoMatch = lazy(() => import('@stateless/NoMatch'))
5151
// const ReactGantt = lazy(() => import('@pages/reactGantt'))
5252
const SandBox = lazy(() => import('@pages/sandBox'))
5353
const Profile = lazy(() => import('@pages/profile'))
54+
const Contact = lazy(() => import('@pages/contact'))
5455

5556
const rootRouter = [
5657
{
@@ -286,6 +287,14 @@ const rootRouter = [
286287
auth: false,
287288
element: lazyLoad(Profile),
288289
},
290+
{
291+
index: false,
292+
path: 'contact',
293+
name: 'Contact',
294+
key: '/contact',
295+
auth: false,
296+
element: lazyLoad(Contact),
297+
},
289298
{
290299
index: false,
291300
path: 'coupons',

0 commit comments

Comments
 (0)