Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #21

Merged
merged 8 commits into from
Jan 1, 2025
Merged
Binary file added public/images/projects/birdy-ai-demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/projects/chainalyze-demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/projects/birdy-ai.png
Binary file not shown.
1 change: 0 additions & 1 deletion public/projects/chainalyze.svg

This file was deleted.

Binary file removed public/projects/demos/birdy-ai-demo.gif
Binary file not shown.
Binary file removed public/projects/demos/chainalyze-demo.gif
Binary file not shown.
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Toaster } from '@/components/ui/toaster';
import Navbar from '@/components/layout/navbar/navbar';
import Footer from '@/components/layout/footer/footer';
import type { Metadata } from 'next';
import './globals.css';
import '../styles/globals.css';

const oxanium = Oxanium({
subsets: ['latin']
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ScrollToTopButton from './scroll-to-top-button';

export default function Footer() {
return (
<footer className="flex w-full flex-col items-center justify-evenly gap-3 border-t border-white/10 bg-white/5 py-6 text-gray-600 md:flex-row">
<footer className="flex w-full flex-col items-center justify-evenly gap-3 border-t border-white/10 bg-white/5 py-6 text-muted md:flex-row">
<p>
&copy; {new Date().getFullYear()} George Burt. All rights
reserved.
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/footer/scroll-to-top-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function ScrollToTopButton() {
};

return (
<div className="cursor-pointer hover:text-gray-500">
<div className="cursor-pointer hover:text-muted-active">
<button onClick={scrollToTop} className="flex gap-1">
<p>Back to top</p>
<ChevronUp />
Expand Down
104 changes: 12 additions & 92 deletions src/components/sections/contact/contact-form.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,18 @@
'use client';

import { FormEvent, useState } from 'react';
import { useToast } from '@/lib/hooks/use-toast';
import useContactForm from '@/lib/hooks/use-contact-form';

export default function ContactForm() {
const { toast } = useToast();
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
honeypot: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);

const handleSubmit = async (event: FormEvent) => {
event.preventDefault();

if (!formData.name || !formData.email || !formData.message) {
toast({
title: 'Missing Required Fields',
description: 'Name, email, and message are all required',
variant: 'destructive'
});
return;
}

if (formData.honeypot) {
toast({
title: 'Failed to send message',
description: 'Spam detected',
variant: 'destructive'
});
return;
}

setIsSubmitting(true);
try {
if (formData.name === 't3st') {
throw new Error('Test error triggered');
}

const response = await fetch('/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});

if (response.status === 429) {
toast({
title: 'Rate Limit Exceeded',
description:
"You've exceeded the rate limit, please try again later.",
variant: 'destructive'
});
setIsSubmitting(false);
return;
}

if (!response.ok) {
throw new Error('Failed to submit form');
}

setIsSubmitting(false);
toast({
title: 'Message sent',
description: 'Your message has been successfully sent!'
});
setFormData({ name: '', email: '', message: '', honeypot: '' });
} catch (error) {
setIsSubmitting(false);
toast({
title: 'Error',
description:
'There was an error sending your message, please try again',
variant: 'destructive'
});
console.error('Error submitting form:', error);
}
};

const handleChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setFormData({
...formData,
[event.target.name]: event.target.value
});
};
const {
formData,
isSubmitting,
handleFormChange,
handleFormSubmit
} = useContactForm();

return (
<form
onSubmit={handleSubmit}
onSubmit={handleFormSubmit}
className="flex flex-grow flex-col gap-6"
>
<p className="flex flex-col gap-2">
Expand All @@ -104,7 +24,7 @@ export default function ContactForm() {
id="name"
name="name"
value={formData.name}
onChange={handleChange}
onChange={handleFormChange}
className="rounded-lg border border-white/10 bg-white/5 p-1 outline-zinc-800 lg:w-4/6"
/>
</p>
Expand All @@ -117,7 +37,7 @@ export default function ContactForm() {
id="email"
name="email"
value={formData.email}
onChange={handleChange}
onChange={handleFormChange}
className="rounded-lg border border-white/10 bg-white/5 p-1 outline-zinc-800 lg:w-4/6"
/>
</p>
Expand All @@ -130,7 +50,7 @@ export default function ContactForm() {
rows={5}
name="message"
value={formData.message}
onChange={handleChange}
onChange={handleFormChange}
className="resize-none rounded-lg border border-white/10 bg-white/5 p-1 outline-zinc-800"
></textarea>
</p>
Expand All @@ -141,7 +61,7 @@ export default function ContactForm() {
type="text"
id="honeypot"
name="honeypot"
onChange={handleChange}
onChange={handleFormChange}
value={formData.honeypot}
/>
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/contact/social-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function SocialCard({
}) {
return (
<Link href={href} target="_blank">
<div className="flex cursor-pointer gap-3 rounded-lg border border-white/10 bg-primary p-2 text-primary-foreground hover:bg-secondary">
<div className="flex cursor-pointer gap-3 rounded-lg border border-white/10 bg-primary p-2 font-semibold text-primary-foreground hover:bg-secondary">
<SocialIcon />
<p>{user}</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/sections/hero/scroll-cue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function ScrollCue() {
return (
<div
className={cn(
'absolute left-1/2 flex -translate-x-1/2 animate-pulse flex-col items-center text-gray-600 transition-all duration-1000 ease-in-out',
'absolute left-1/2 flex -translate-x-1/2 animate-pulse flex-col items-center text-muted transition-all duration-1000 ease-in-out',
isMobile ? 'bottom-24' : 'bottom-7',
isVisible
? 'translate-y-0 opacity-100'
Expand Down
6 changes: 3 additions & 3 deletions src/components/sections/projects/project-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export default function ProjectCard({
return (
<article className="flex h-full flex-col items-center rounded-lg border border-white/10">
<Image
height={175}
width={175}
height={1909}
width={933}
src={image}
alt={`${title} image`}
className="mb-3 mt-5 object-contain md:h-[200px] md:w-[200px]"
className="mb-3 rounded-t-lg object-cover"
/>
<div className="flex flex-col gap-2 p-6">
<h3 className="text-[1.5rem] font-semibold md:text-left md:text-[1.75rem]">
Expand Down
15 changes: 6 additions & 9 deletions src/components/sections/skills/skills-content.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import Link from 'next/link';
import Image from 'next/image';
import Tooltip from './tooltip';
import Card from '@/components/ui/card';
import Marquee from 'react-fast-marquee';
import { Separator } from '@/components/ui/separator';
import { SKILLS_CONTENT } from '@/lib/constants/content-constants';
import {
SKILLS,
SECONDARY_SKILLS
} from '@/lib/constants/skills-constants';
import Marquee from 'react-fast-marquee';
import { Separator } from '@/components/ui/separator';
import { SKILLS_CONTENT } from '@/lib/constants/content-constants';

export default function SkillsContent() {
return (
Expand Down Expand Up @@ -42,9 +43,7 @@ export default function SkillsContent() {
src={skill.image}
alt={`${skill.label} logo`}
/>
<span className="absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-black/60 px-2 py-1 text-sm text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100">
{skill.label}
</span>
<Tooltip label={skill.label} />
</Link>
))}
</div>
Expand All @@ -69,9 +68,7 @@ export default function SkillsContent() {
src={skill.image}
alt={`${skill.label} logo`}
/>
<span className="absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-black/80 px-2 py-1 text-sm text-white opacity-0 transition-opacity duration-300 group-hover:opacity-100">
{skill.label}
</span>
<Tooltip label={skill.label} />
</Link>
))}
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/components/sections/skills/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Tooltip({ label }: { label: string }) {
return (
<span className="absolute -bottom-8 left-1/2 -translate-x-1/2 whitespace-nowrap rounded bg-primary/90 px-2 py-1 text-sm text-black opacity-0 transition-opacity duration-300 group-hover:opacity-100">
{label}
</span>
);
}
Loading
Loading