Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
portfolio
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
QuanMB
portfolio
Commits
c6cf4cad
Commit
c6cf4cad
authored
Jun 29, 2026
by
QuanMB
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
clean(contact): remove email submission form and center contact cards
parent
77d8d700
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
54 additions
and
205 deletions
+54
-205
Contact.tsx
src/components/Contact/Contact.tsx
+54
-205
No files found.
src/components/Contact/Contact.tsx
View file @
c6cf4cad
"use client"
;
"use client"
;
import
{
useState
}
from
"react"
;
export
default
function
Contact
()
{
export
default
function
Contact
()
{
const
[
formData
,
setFormData
]
=
useState
({
name
:
""
,
email
:
""
,
message
:
""
,
});
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
success
,
setSuccess
]
=
useState
(
false
);
const
[
error
,
setError
]
=
useState
<
string
|
null
>
(
null
);
const
handleInputChange
=
(
e
:
React
.
ChangeEvent
<
HTMLInputElement
|
HTMLTextAreaElement
>
)
=>
{
const
{
name
,
value
}
=
e
.
target
;
setFormData
((
prev
)
=>
({
...
prev
,
[
name
]:
value
}));
if
(
error
)
setError
(
null
);
};
const
handleSubmit
=
async
(
e
:
React
.
FormEvent
)
=>
{
e
.
preventDefault
();
setError
(
null
);
if
(
!
formData
.
name
.
trim
()
||
!
formData
.
email
.
trim
()
||
!
formData
.
message
.
trim
())
{
setError
(
"Please fill in all the fields."
);
return
;
}
const
emailRegex
=
/^
[^\s
@
]
+@
[^\s
@
]
+
\.[^\s
@
]
+$/
;
if
(
!
emailRegex
.
test
(
formData
.
email
))
{
setError
(
"Please enter a valid email address."
);
return
;
}
setLoading
(
true
);
try
{
await
new
Promise
((
resolve
)
=>
setTimeout
(
resolve
,
1500
));
setSuccess
(
true
);
setFormData
({
name
:
""
,
email
:
""
,
message
:
""
});
setTimeout
(()
=>
{
setSuccess
(
false
);
},
5000
);
}
catch
(
err
)
{
setError
(
"Something went wrong. Please try again."
);
}
finally
{
setLoading
(
false
);
}
};
return
(
return
(
<
section
id=
"contact"
className=
"py-32 px-6 max-w-[1
200px] mx-auto relative
"
>
<
section
id=
"contact"
className=
"py-32 px-6 max-w-[1
000px] mx-auto relative text-center
"
>
{
/* Header */
}
{
/* Header */
}
<
div
className=
"mb-14
text-left
"
>
<
div
className=
"mb-14"
>
<
span
className=
"text-xs font-semibold uppercase text-primary tracking-widest block"
>
Get In Touch
</
span
>
<
span
className=
"text-xs font-semibold uppercase text-primary tracking-widest block"
>
Get In Touch
</
span
>
<
h2
className=
"text-3xl sm:text-4xl font-extrabold mt-2 text-text-primary tracking-tight"
>
Let
'
s Connect
</
h2
>
<
h2
className=
"text-3xl sm:text-4xl font-extrabold mt-2 text-text-primary tracking-tight"
>
Let
'
s Connect
</
h2
>
</
div
>
</
div
>
{
/* Content Grid */
}
<
p
className=
"text-base leading-relaxed text-text-secondary max-w-[600px] mx-auto mb-12"
>
<
div
className=
"grid grid-cols-1 lg:grid-cols-[0.9fr_1.1fr] gap-16 items-start"
>
{
/* Info Column */
}
<
div
className=
"flex flex-col gap-6"
>
<
p
className=
"text-[15px] leading-relaxed text-text-secondary"
>
I am currently open to new opportunities, internship positions, or collaborations.
I am currently open to new opportunities, internship positions, or collaborations.
If you have a question or just want to say hi, feel free to drop a message
!
If you have a question or just want to say hi, feel free to reach out through any of these channels
!
</
p
>
</
p
>
<
div
className=
"flex flex-col gap-4 my-4"
>
{
/* Grid of Cards */
}
<
div
className=
"grid grid-cols-1 md:grid-cols-3 gap-6 mb-12"
>
{
/* Email Card */
}
{
/* Email Card */
}
<
div
className=
"bg-surface border border-surface-border flex items-center gap-4 p-5 rounded-2xl
"
>
<
div
className=
"bg-surface border border-surface-border flex flex-col items-center text-center p-6 rounded-2xl transition-all duration-300 hover:border-primary/20
"
>
<
div
className=
"w-10 h-10 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary
flex-shrink-0"
>
<
div
className=
"w-12 h-12 rounded-2xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary mb-4
flex-shrink-0"
>
<
svg
width=
"18"
height=
"18
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
svg
width=
"20"
height=
"20
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
path
d=
"M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
></
path
>
<
path
d=
"M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"
></
path
>
<
polyline
points=
"22,6 12,13 2,6"
></
polyline
>
<
polyline
points=
"22,6 12,13 2,6"
></
polyline
>
</
svg
>
</
svg
>
</
div
>
</
div
>
<
div
className=
"flex flex-col"
>
<
span
className=
"text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-1.5"
>
Email Me
</
span
>
<
span
className=
"text-[9px] font-bold text-text-secondary uppercase tracking-widest"
>
Email Me
</
span
>
<
a
href=
"mailto:mquan347bbb@gmail.com"
className=
"text-sm font-semibold text-text-primary hover:text-primary transition-colors duration-205 break-all"
>
<
a
href=
"mailto:mquan347bbb@gmail.com"
className=
"text-sm font-semibold text-text-primary mt-1 hover:text-primary transition-colors duration-205"
>
mquan347bbb@gmail.com
</
a
>
mquan347bbb@gmail.com
</
div
>
</
a
>
</
div
>
</
div
>
{
/* Phone Card */
}
{
/* Phone Card */
}
<
div
className=
"bg-surface border border-surface-border flex items-center gap-4 p-5 rounded-2xl
"
>
<
div
className=
"bg-surface border border-surface-border flex flex-col items-center text-center p-6 rounded-2xl transition-all duration-300 hover:border-primary/20
"
>
<
div
className=
"w-10 h-10 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary
flex-shrink-0"
>
<
div
className=
"w-12 h-12 rounded-2xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary mb-4
flex-shrink-0"
>
<
svg
width=
"18"
height=
"18
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
svg
width=
"20"
height=
"20
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
path
d=
"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"
></
path
>
<
path
d=
"M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"
></
path
>
</
svg
>
</
svg
>
</
div
>
</
div
>
<
div
className=
"flex flex-col"
>
<
span
className=
"text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-1.5"
>
Call Me
</
span
>
<
span
className=
"text-[9px] font-bold text-text-secondary uppercase tracking-widest"
>
Call Me
</
span
>
<
a
href=
"tel:0354325496"
className=
"text-sm font-semibold text-text-primary hover:text-primary transition-colors duration-205"
>
<
a
href=
"tel:0354325496"
className=
"text-sm font-semibold text-text-primary mt-1 hover:text-primary transition-colors duration-205"
>
0354325496
</
a
>
0354325496
</
div
>
</
a
>
</
div
>
</
div
>
{
/* Location Card */
}
{
/* Location Card */
}
<
div
className=
"bg-surface border border-surface-border flex items-center gap-4 p-5 rounded-2xl
"
>
<
div
className=
"bg-surface border border-surface-border flex flex-col items-center text-center p-6 rounded-2xl transition-all duration-300 hover:border-primary/20
"
>
<
div
className=
"w-10 h-10 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary
flex-shrink-0"
>
<
div
className=
"w-12 h-12 rounded-2xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary mb-4
flex-shrink-0"
>
<
svg
width=
"18"
height=
"18
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
svg
width=
"20"
height=
"20
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
path
d=
"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"
></
path
>
<
path
d=
"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"
></
path
>
<
circle
cx=
"12"
cy=
"10"
r=
"3"
></
circle
>
<
circle
cx=
"12"
cy=
"10"
r=
"3"
></
circle
>
</
svg
>
</
svg
>
</
div
>
</
div
>
<
div
className=
"flex flex-col"
>
<
span
className=
"text-[10px] font-bold text-text-secondary uppercase tracking-widest mb-1.5"
>
Location
</
span
>
<
span
className=
"text-[9px] font-bold text-text-secondary uppercase tracking-widest"
>
Location
</
span
>
<
span
className=
"text-sm font-semibold text-text-primary"
>
<
span
className=
"text-sm font-semibold text-text-primary mt-1"
>
Ho Chi Minh City, Vietnam
</
span
>
Ho Chi Minh City, Vietnam
</
div
>
</
span
>
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Socials */
}
{
/* Socials */
}
<
div
className=
"flex flex-col gap-3 mt-4
"
>
<
div
className=
"flex flex-col items-center gap-3
"
>
<
span
className=
"text-xs font-semibold text-text-secondary uppercase tracking-wider"
>
Connect on Socials
</
span
>
<
span
className=
"text-xs font-semibold text-text-secondary uppercase tracking-wider"
>
Connect on Socials
</
span
>
<
div
className=
"flex
gap-4"
>
<
div
className=
"flex justify-center
gap-4"
>
<
a
href=
"https://github.com/BMQuan"
target=
"_blank"
rel=
"noopener noreferrer"
className=
"w-9 h-9
rounded-full bg-surface border border-surface-border flex items-center justify-center text-text-secondary hover:border-primary hover:text-text-primary transition-all duration-200"
aria
-
label=
"GitHub"
>
<
a
href=
"https://github.com/BMQuan"
target=
"_blank"
rel=
"noopener noreferrer"
className=
"w-10 h-10
rounded-full bg-surface border border-surface-border flex items-center justify-center text-text-secondary hover:border-primary hover:text-text-primary transition-all duration-200"
aria
-
label=
"GitHub"
>
<
svg
width=
"18"
height=
"18
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
svg
width=
"20"
height=
"20
"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
path
d=
"M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
></
path
>
<
path
d=
"M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
></
path
>
</
svg
>
</
svg
>
</
a
>
</
a
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
{
/* Form Column */
}
<
div
className=
"bg-surface border border-surface-border p-8 sm:p-10 rounded-2xl shadow-sm"
>
{
/* Success Banner */
}
{
success
&&
(
<
div
className=
"flex items-center gap-3 bg-emerald-500/5 border border-emerald-500/20 rounded-xl p-4 text-emerald-400 text-xs font-medium mb-6 animate-fade-in"
>
<
svg
className=
"flex-shrink-0"
width=
"18"
height=
"18"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
path
d=
"M22 11.08V12a10 10 0 1 1-5.93-9.14"
></
path
>
<
polyline
points=
"22 4 12 14.01 9 11.01"
></
polyline
>
</
svg
>
<
span
>
Thank you! Your message has been sent successfully.
</
span
>
</
div
>
)
}
{
/* Validation Error Banner */
}
{
error
&&
(
<
div
className=
"flex items-center gap-3 bg-rose-500/5 border border-rose-500/20 rounded-xl p-4 text-rose-400 text-xs font-medium mb-6 animate-fade-in"
>
<
svg
className=
"flex-shrink-0"
width=
"18"
height=
"18"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
circle
cx=
"12"
cy=
"12"
r=
"10"
></
circle
>
<
line
x1=
"12"
y1=
"8"
x2=
"12"
y2=
"12"
></
line
>
<
line
x1=
"12"
y1=
"16"
x2=
"12.01"
y2=
"16"
></
line
>
</
svg
>
<
span
>
{
error
}
</
span
>
</
div
>
)
}
<
form
onSubmit=
{
handleSubmit
}
className=
"flex flex-col gap-5"
>
{
/* Name */
}
<
div
className=
"flex flex-col gap-2"
>
<
label
htmlFor=
"name"
className=
"text-[10px] font-bold uppercase tracking-wider text-text-secondary"
>
Your Name
</
label
>
<
input
type=
"text"
id=
"name"
name=
"name"
value=
{
formData
.
name
}
onChange=
{
handleInputChange
}
className=
"bg-dark-bg border border-surface-border rounded-xl p-3.5 px-4 font-inter text-sm text-text-primary placeholder-text-secondary/40 transition-all duration-200 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary disabled:opacity-50"
placeholder=
"e.g. John Doe"
disabled=
{
loading
}
/>
</
div
>
{
/* Email */
}
<
div
className=
"flex flex-col gap-2"
>
<
label
htmlFor=
"email"
className=
"text-[10px] font-bold uppercase tracking-wider text-text-secondary"
>
Your Email
</
label
>
<
input
type=
"email"
id=
"email"
name=
"email"
value=
{
formData
.
email
}
onChange=
{
handleInputChange
}
className=
"bg-dark-bg border border-surface-border rounded-xl p-3.5 px-4 font-inter text-sm text-text-primary placeholder-text-secondary/40 transition-all duration-200 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary disabled:opacity-50"
placeholder=
"e.g. johndoe@gmail.com"
disabled=
{
loading
}
/>
</
div
>
{
/* Message */
}
<
div
className=
"flex flex-col gap-2"
>
<
label
htmlFor=
"message"
className=
"text-[10px] font-bold uppercase tracking-wider text-text-secondary"
>
Message
</
label
>
<
textarea
id=
"message"
name=
"message"
value=
{
formData
.
message
}
onChange=
{
handleInputChange
}
className=
"bg-dark-bg border border-surface-border rounded-xl p-3.5 px-4 font-inter text-sm text-text-primary placeholder-text-secondary/40 transition-all duration-200 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary disabled:opacity-50 min-h-[120px] resize-y"
placeholder=
"Hello! I would like to discuss..."
disabled=
{
loading
}
></
textarea
>
</
div
>
{
/* Submit Button */
}
<
button
type=
"submit"
className=
"bg-primary hover:bg-blue-600 text-white rounded-full font-semibold text-sm px-6 py-3.5 flex items-center justify-center gap-2 cursor-pointer w-full transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed shadow-sm"
disabled=
{
loading
}
>
{
loading
?
(
<>
<
span
className=
"w-[16px] h-[16px] border-2 border-white/30 border-t-white rounded-full animate-spin"
></
span
>
Sending...
</>
)
:
(
<>
Send Message
<
svg
width=
"16"
height=
"16"
viewBox=
"0 0 24 24"
fill=
"none"
stroke=
"currentColor"
strokeWidth=
"2.5"
strokeLinecap=
"round"
strokeLinejoin=
"round"
>
<
line
x1=
"22"
y1=
"2"
x2=
"11"
y2=
"13"
></
line
>
<
polygon
points=
"22 2 15 22 11 13 2 9 22 2"
></
polygon
>
</
svg
>
</>
)
}
</
button
>
</
form
>
</
div
>
</
div
>
</
section
>
</
section
>
);
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment