Skip to content

Commit

Permalink
feat: create contact section (#27)
Browse files Browse the repository at this point in the history
* feat: create contact form

* feat: create footer

* feat: add form validation
  • Loading branch information
Skolaczk authored Feb 14, 2024
1 parent ad8b85e commit dde5564
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { About } from '@/components/about';
import { Contact } from '@/components/contact';
import { Experience } from '@/components/experience';
import { Footer } from '@/components/footer';
import { Intro } from '@/components/intro';
import { Projects } from '@/components/projects';
import { ThemeToggle } from '@/components/theme-toggle';
Expand All @@ -13,6 +15,8 @@ const Home = () => {
<About />
<Projects />
<Experience />
<Contact />
<Footer />
</div>
<div className="fixed bottom-5 right-5 sm:bottom-8 sm:right-8">
<ThemeToggle />
Expand Down
85 changes: 85 additions & 0 deletions src/components/contact.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use client';

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

import { Button } from '@/components/button';
import { Icons } from '@/components/icons';
import { SectionHeading } from '@/components/section-heading';
import { cn } from '@/lib/utils';

const formSchema = z.object({
email: z
.string()
.min(1, { message: 'Email is required' })
.email({ message: 'Must be a valid email' }),
message: z.string().min(1, { message: 'Message is required' }),
});

type TFormSchema = z.infer<typeof formSchema>;

export const Contact = () => {
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<TFormSchema>({ resolver: zodResolver(formSchema) });

const onSubmit = (values: TFormSchema) => {
console.log(values);

reset();
};

return (
<section className="my-8 w-full sm:my-10">
<SectionHeading
heading="Get In Touch"
content="Please contact me directly at [email protected] or through this form."
/>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col items-center gap-5"
>
<div className="w-full max-w-xl">
<input
type="email"
id="email"
placeholder="Your email"
{...register('email')}
className={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
errors.email?.message && 'border-destructive'
)}
/>
{errors.email?.message && (
<p className="text-destructive mt-1 text-sm">
{errors.email?.message}
</p>
)}
</div>
<div className="w-full max-w-xl">
<textarea
id="message"
placeholder="Your message"
{...register('message')}
className={cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-60 w-full resize-none rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
errors.email?.message && 'border-destructive'
)}
></textarea>
{errors.message?.message && (
<p className="text-destructive mt-1 text-sm">
{errors.message?.message}
</p>
)}
</div>
<Button size="lg">
Submit <Icons.arrowRight className="ml-2 size-4" />
</Button>
</form>
</section>
);
};
13 changes: 13 additions & 0 deletions src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Button } from '@/components/button';

export const Footer = () => {
return (
<footer className="text-muted-foreground my-2 text-sm">
© {new Date().getFullYear()}{' '}
<Button variant="link" className="text-muted-foreground p-0 font-medium">
<a href="#">Michał Skolak</a>
</Button>
. All rights reserved.
</footer>
);
};
2 changes: 1 addition & 1 deletion src/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const Icons = {
download: Download,
preview: ExternalLink,
githubOutline: Github,
briefcase: Briefcase,
github: (props: LucideProps) => (
<svg viewBox="0 0 24 24" {...props}>
<path
Expand All @@ -32,7 +33,6 @@ export const Icons = {
/>
</svg>
),
briefcase: Briefcase,
html: (props: LucideProps) => (
<svg viewBox="0 0 50 50" {...props}>
<path
Expand Down
4 changes: 3 additions & 1 deletion src/components/section-heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export const SectionHeading = ({ heading, content }: TProps) => {
return (
<div className="mb-5 text-center">
<h2 className="text-3xl font-medium tracking-tighter">{heading}</h2>
{content && <p className="text-muted-foreground mt-2">{content}</p>}
{content && (
<p className="text-muted-foreground mt-2 text-sm">{content}</p>
)}
</div>
);
};

0 comments on commit dde5564

Please sign in to comment.