Merge pull request #2 from mr-shortman/dev
New Hero and Tech Section; Mobile, Responsive
This commit is contained in:
commit
1ffad8a9ba
BIN
public/hero.gif
Normal file
BIN
public/hero.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@ -1,5 +1,10 @@
|
||||
import { TechIcon } from "./components/setions/tech";
|
||||
|
||||
export type SocialIcon = "github" | "discord";
|
||||
|
||||
type AppConfig = {
|
||||
navigator: { label: string; path: string }[];
|
||||
socials: { name: string; icon: SocialIcon; link: string }[];
|
||||
};
|
||||
|
||||
export const appConfig: AppConfig = {
|
||||
@ -10,13 +15,32 @@ export const appConfig: AppConfig = {
|
||||
},
|
||||
{
|
||||
label: "Projects",
|
||||
path: "/#projects",
|
||||
path: "/projects",
|
||||
},
|
||||
{
|
||||
label: "Contact",
|
||||
path: "/contact/#",
|
||||
},
|
||||
],
|
||||
socials: [
|
||||
{
|
||||
name: "discord",
|
||||
icon: "discord",
|
||||
link: "https://discord.com",
|
||||
},
|
||||
{
|
||||
name: "github",
|
||||
icon: "github",
|
||||
link: "https://github.com",
|
||||
},
|
||||
{
|
||||
name: "github",
|
||||
icon: "github",
|
||||
link: "https://github.com",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const appNavigator = appConfig.navigator;
|
||||
|
||||
export const socials = appConfig.socials;
|
||||
|
||||
@ -1,21 +1,8 @@
|
||||
import MeCard from "@/components/me-card";
|
||||
import Contact from "@/components/setions/contact";
|
||||
import React from "react";
|
||||
import Contact from "@/components/setions/contact";
|
||||
|
||||
function ContactPage() {
|
||||
return (
|
||||
<div className="flex gap-12 lg:gap-20 container ">
|
||||
<MeCard
|
||||
className="sticky top-20"
|
||||
button={{
|
||||
label: "Email Me",
|
||||
link: `mailto:${process.env.NEXT_PUBLIC_EMAIL}`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Contact />
|
||||
</div>
|
||||
);
|
||||
return <Contact />;
|
||||
}
|
||||
|
||||
export default ContactPage;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import Navbar from "@/components/navbar";
|
||||
import Footer from "@/components/footer";
|
||||
|
||||
function Layout({ children }: { children: JSX.Element }) {
|
||||
return (
|
||||
@ -10,6 +11,8 @@ function Layout({ children }: { children: JSX.Element }) {
|
||||
|
||||
{/* <div className="absolute w-1 h-screen top-0 left-1/2 -translate-x-1/2 transform bg-foreground" /> */}
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import { Hero, Projects } from "@/components/setions";
|
||||
import { Hero, Projects, Tech } from "@/components/setions";
|
||||
import Contact from "@/components/setions/contact";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Hero />
|
||||
<div className="w-full mt-64">
|
||||
<div className="pt-12 lg:pt-40">
|
||||
<Hero />
|
||||
</div>
|
||||
<div className="w-full mt-20 lg:mt-44 space-y-12">
|
||||
<Tech />
|
||||
<Projects />
|
||||
|
||||
<Contact withoutMeCard />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
14
src/app/(__PAGES__)/projects/page.tsx
Normal file
14
src/app/(__PAGES__)/projects/page.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Projects } from "@/components/setions";
|
||||
import Contact from "@/components/setions/contact";
|
||||
import React from "react";
|
||||
|
||||
function ProjectsPage() {
|
||||
return (
|
||||
<>
|
||||
<Projects />
|
||||
<Contact withoutMeCard />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProjectsPage;
|
||||
@ -65,13 +65,6 @@ body {
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply dark bg-background text-foreground;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Poppins";
|
||||
src: url("./fonts/Poppins-Regular.ttf") format("truetype");
|
||||
@ -85,6 +78,18 @@ body {
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply dark bg-background text-foreground;
|
||||
}
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.halftone {
|
||||
--dotSize: 0.25rem;
|
||||
@ -122,7 +127,3 @@ body {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
132
src/components/contact-form.tsx
Normal file
132
src/components/contact-form.tsx
Normal file
@ -0,0 +1,132 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Heading from "./heading";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Button } from "@/ui/button";
|
||||
import { Textarea } from "@/ui/textarea";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/ui/form";
|
||||
import { Input } from "@/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2).max(50),
|
||||
email: z.string().email(),
|
||||
budget: z.string(),
|
||||
message: z.string().max(500),
|
||||
});
|
||||
|
||||
function ContactForm() {
|
||||
// 1. Define your form.
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
||||
// 2. Define a submit handler.
|
||||
function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
// Do something with the form values.
|
||||
// ✅ This will be type-safe and validated.
|
||||
console.log(values);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||
<div className="flex gap-4 w-full">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full ">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="John Doe" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="email@example.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="budget"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Budget</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
"w-full",
|
||||
field.value?.length
|
||||
? "text-foreground"
|
||||
: "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
<SelectValue placeholder="Select your Budget" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="< €2k">{"less then $2k"}</SelectItem>
|
||||
<SelectItem value="> €4k">{"more then $4k"}</SelectItem>
|
||||
<SelectItem value="> €6k">{"more then $6k"}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="message"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Message</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea rows={4} placeholder="Message..." {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit" className="w-full">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContactForm;
|
||||
32
src/components/footer.tsx
Normal file
32
src/components/footer.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import Link from "next/link";
|
||||
import React from "react";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer className="container p-4 flex flex-col md:flex-row items-center justify-between mt-12">
|
||||
<span className="text-sm">
|
||||
Copyright © {new Date().getFullYear()}. All rights reserved.
|
||||
</span>
|
||||
|
||||
<menu className="flex items-center gap-4">
|
||||
<Button
|
||||
asChild
|
||||
variant={"link"}
|
||||
className="text-muted-foreground text-xs hover:text-foreground px-0 w-max"
|
||||
>
|
||||
<Link href={"/imprint"}>Imprint</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
variant={"link"}
|
||||
className="text-muted-foreground text-xs hover:text-foreground px-0 w-max"
|
||||
>
|
||||
<Link href={"/privacy"}>Privacy</Link>
|
||||
</Button>
|
||||
</menu>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
@ -1,8 +1,22 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import React from "react";
|
||||
|
||||
function Heading({ title, subTitle }: { title: string; subTitle?: string }) {
|
||||
function Heading({
|
||||
title,
|
||||
subTitle,
|
||||
className,
|
||||
}: {
|
||||
title: string;
|
||||
subTitle?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<h2 className="text-8xl pb-4 font-black order-last sticky top-0 z-50 bg-background">
|
||||
<h2
|
||||
className={cn(
|
||||
"text-5xl lg:text-8xl text-center md:text-left pb-4 font-black bg-background",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<span className="text-muted-foreground">{subTitle}</span>
|
||||
<br />
|
||||
{title}
|
||||
|
||||
@ -3,6 +3,36 @@ import { Button } from "./ui/button";
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils";
|
||||
import Link from "next/link";
|
||||
import { SocialIcon, socials } from "@/app.config";
|
||||
|
||||
const icons = {
|
||||
github: (props: any) => (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 220 220"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M110 18.3333C97.9621 18.3333 86.0421 20.7043 74.9206 25.311C63.7991 29.9176 53.6938 36.6698 45.1818 45.1818C27.991 62.3726 18.3333 85.6884 18.3333 110C18.3333 150.517 44.6416 184.892 81.0332 197.083C85.6166 197.817 87.0832 194.975 87.0832 192.5V177.008C61.6916 182.508 56.2832 164.725 56.2832 164.725C52.0666 154.092 46.1083 151.25 46.1083 151.25C37.7666 145.567 46.7499 145.75 46.7499 145.75C55.9166 146.392 60.7749 155.192 60.7749 155.192C68.7499 169.125 82.2249 165 87.4499 162.8C88.2749 156.842 90.6582 152.808 93.2249 150.517C72.8749 148.225 51.5166 140.342 51.5166 105.417C51.5166 95.2416 54.9999 87.0832 60.9582 80.5749C60.0416 78.2832 56.8332 68.7499 61.8749 56.3749C61.8749 56.3749 69.5749 53.8999 87.0832 65.7249C94.3249 63.7082 102.208 62.6999 110 62.6999C117.792 62.6999 125.675 63.7082 132.917 65.7249C150.425 53.8999 158.125 56.3749 158.125 56.3749C163.167 68.7499 159.958 78.2832 159.042 80.5749C165 87.0832 168.483 95.2416 168.483 105.417C168.483 140.433 147.033 148.133 126.592 150.425C129.892 153.267 132.917 158.858 132.917 167.383V192.5C132.917 194.975 134.383 197.908 139.058 197.083C175.45 184.8 201.667 150.517 201.667 110C201.667 97.9621 199.296 86.0421 194.689 74.9206C190.082 63.7991 183.33 53.6938 174.818 45.1818C166.306 36.6698 156.201 29.9176 145.079 25.311C133.958 20.7043 122.038 18.3333 110 18.3333Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
discord: (props: any) => (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 220 220"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M176.642 48.8584C164.45 43.1751 151.25 39.0501 137.5 36.6667C137.258 36.6702 137.028 36.769 136.858 36.9417C135.208 39.9667 133.283 43.9084 132 46.9334C117.416 44.7348 102.584 44.7348 88 46.9334C86.7167 43.8167 84.7917 39.9667 83.05 36.9417C82.9584 36.7584 82.6834 36.6667 82.4084 36.6667C68.6584 39.0501 55.55 43.1751 43.2667 48.8584C43.175 48.8584 43.0834 48.9501 42.9917 49.0417C18.0584 86.3501 11.1833 122.65 14.575 158.583C14.575 158.767 14.6667 158.95 14.85 159.042C31.35 171.142 47.2084 178.475 62.8834 183.333C63.1584 183.425 63.4334 183.333 63.525 183.15C67.1917 178.108 70.4917 172.792 73.3334 167.2C73.5167 166.833 73.3334 166.467 72.9667 166.375C67.7417 164.358 62.7917 161.975 57.9334 159.225C57.5667 159.042 57.5667 158.492 57.8417 158.217C58.85 157.483 59.8584 156.658 60.8667 155.925C61.05 155.742 61.325 155.742 61.5084 155.833C93.0417 170.225 127.05 170.225 158.217 155.833C158.4 155.742 158.675 155.742 158.858 155.925C159.867 156.75 160.875 157.483 161.883 158.308C162.25 158.583 162.25 159.133 161.792 159.317C157.025 162.158 151.983 164.45 146.758 166.467C146.392 166.558 146.3 167.017 146.392 167.292C149.325 172.883 152.625 178.2 156.2 183.242C156.475 183.333 156.75 183.425 157.025 183.333C172.792 178.475 188.65 171.142 205.15 159.042C205.333 158.95 205.425 158.767 205.425 158.583C209.458 117.058 198.733 81.0334 177.008 49.0417C176.917 48.9501 176.825 48.8584 176.642 48.8584ZM78.1 136.675C68.6584 136.675 60.775 127.967 60.775 117.242C60.775 106.517 68.475 97.8084 78.1 97.8084C87.8167 97.8084 95.5167 106.608 95.425 117.242C95.425 127.967 87.725 136.675 78.1 136.675ZM141.992 136.675C132.55 136.675 124.667 127.967 124.667 117.242C124.667 106.517 132.367 97.8084 141.992 97.8084C151.708 97.8084 159.408 106.608 159.317 117.242C159.317 127.967 151.708 136.675 141.992 136.675Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
|
||||
function MeCard({
|
||||
className,
|
||||
@ -22,12 +52,7 @@ function MeCard({
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="h-64 w-3/4 mx-auto relative rounded-xl overflow-hidden z-10">
|
||||
<Image
|
||||
src="/me.jpg"
|
||||
alt="me"
|
||||
fill
|
||||
className="object-cover scale-105 transition-all duration-300 group-hover:scale-100"
|
||||
/>
|
||||
<Image src="/me.jpg" alt="me" fill className="object-cover " />
|
||||
</div>
|
||||
|
||||
<div className="text-center flex flex-col items-center">
|
||||
@ -35,13 +60,27 @@ function MeCard({
|
||||
<span className="text-emerald-500 text-sm -translate-y-px">
|
||||
Available for work
|
||||
</span>
|
||||
<Button className="mt-4" asChild>
|
||||
{/* <Button className="mt-4" asChild>
|
||||
<Link href={button.link}>{button.label}</Link>
|
||||
</Button>
|
||||
</Button> */}
|
||||
<menu className="w-full flex justify-center items-center gap-2 pt-4">
|
||||
{socials.map((s, idx) => {
|
||||
const Icon = icons[s.icon];
|
||||
return (
|
||||
<li key={idx}>
|
||||
<Link href={s.link} target="_blank">
|
||||
<div className="p-2 rounded-full bg-background flex items-center justify-center hover:scale-105 transition-all duration-150">
|
||||
{<Icon className="size-6" />}
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-center text-muted-foreground text-lg font-bold">
|
||||
<p className="text-center text-muted-foreground text-sm md:text-lg font-bold">
|
||||
A Software Engineer who has developed countless innovative solutions.
|
||||
</p>
|
||||
|
||||
|
||||
@ -1,24 +1,59 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { Button } from "@/ui/button";
|
||||
import Link from "next/link";
|
||||
import NavigationBreadcrumbs from "./navigation-breadcrumbs";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { appNavigator } from "@/app.config";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Navbar() {
|
||||
const pathname = usePathname();
|
||||
const isActive = (path: string) => pathname === path.replace(/\/#/g, "");
|
||||
return (
|
||||
<nav className="container py-4 flex justify-between items-center ">
|
||||
<nav className="container p-4 flex justify-between items-center ">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link
|
||||
className="text-sm"
|
||||
className="text-sm hidden md:block"
|
||||
href={`mailto:${process.env.NEXT_PUBLIC_EMAIL}`}
|
||||
>
|
||||
{process.env.NEXT_PUBLIC_EMAIL}
|
||||
</Link>
|
||||
<Button asChild size={"sm"}>
|
||||
<Link href={"/contact"}>Work with me</Link>
|
||||
</Button>
|
||||
{!isActive("/contact") && (
|
||||
<Button asChild size={"sm"} className="hidden md:flex">
|
||||
<Link href={"/contact"}>Work with me</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<NavigationBreadcrumbs />
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{appNavigator.map(({ label, path }, idx) => (
|
||||
<div key={path} className="flex items-center gap-2">
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
className={cn(isActive(path) && "text-foreground")}
|
||||
href={path}
|
||||
>
|
||||
{label}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
{idx + 1 < appNavigator.length && (
|
||||
<BreadcrumbSeparator>
|
||||
<span>/</span>
|
||||
</BreadcrumbSeparator>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
"use client";
|
||||
import { usePathname } from "next/navigation";
|
||||
import React from "react";
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import { appNavigator } from "@/app.config";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function NavigationBreadcrumbs() {
|
||||
const pathname = usePathname();
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{appNavigator.map(({ label, path }, idx) => (
|
||||
<div key={path} className="flex items-center gap-2">
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink
|
||||
className={cn(
|
||||
pathname === path.replace(/\/#/g, "") && "text-foreground"
|
||||
)}
|
||||
href={path}
|
||||
>
|
||||
{" "}
|
||||
{label}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
{idx + 1 < appNavigator.length && (
|
||||
<BreadcrumbSeparator>
|
||||
<span>/</span>
|
||||
</BreadcrumbSeparator>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavigationBreadcrumbs;
|
||||
@ -1,127 +1,24 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import MeCard from "../me-card";
|
||||
import Heading from "../heading";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Button } from "@/ui/button";
|
||||
import { Textarea } from "@/ui/textarea";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/ui/form";
|
||||
import { Input } from "@/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2).max(50),
|
||||
email: z.string().email(),
|
||||
budget: z.string(),
|
||||
message: z.string().max(500),
|
||||
});
|
||||
|
||||
function Contact() {
|
||||
// 1. Define your form.
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
||||
// 2. Define a submit handler.
|
||||
function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
// Do something with the form values.
|
||||
// ✅ This will be type-safe and validated.
|
||||
console.log(values);
|
||||
}
|
||||
import ContactForm from "../contact-form";
|
||||
|
||||
function Contact({ withoutMeCard }: { withoutMeCard?: boolean }) {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Heading subTitle="Let's Work" title="Together" />
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
|
||||
<div className="flex gap-4 w-full">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full ">
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="John Doe" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="email@example.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="budget"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Budget</FormLabel>
|
||||
<FormControl>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue
|
||||
className="placeholder:text-muted-foreground"
|
||||
placeholder="Select your Budget"
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="light">{"< €2k"}</SelectItem>
|
||||
<SelectItem value="dark">Dark</SelectItem>
|
||||
<SelectItem value="system">System</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="message"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Message</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea rows={4} placeholder="Message..." {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit" className="w-full">
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
<div className="flex flex-col lg:flex-row gap-12 lg:gap-20 container ">
|
||||
{!withoutMeCard && (
|
||||
<MeCard
|
||||
className="sticky top-20"
|
||||
button={{
|
||||
label: "Email Me",
|
||||
link: `mailto:${process.env.NEXT_PUBLIC_EMAIL}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="w-full space-y-4 p-4">
|
||||
<Heading subTitle="Let's Work" title="Together" />
|
||||
<ContactForm />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
function CV() {
|
||||
return <div>CV</div>;
|
||||
}
|
||||
|
||||
export default CV;
|
||||
@ -8,79 +8,51 @@ function Hero() {
|
||||
const [hovered, setHovered] = React.useState("");
|
||||
|
||||
const textHover =
|
||||
"hover:text-primary hover:scale-105 transition-all duration-150";
|
||||
"hover:text-primary hover:scale-105 transition-all duration-300";
|
||||
return (
|
||||
<div className="text-7xl rounded-md group font-bold max-w-xl mx-auto text-center flex flex-col relative cursor-default translate-x-16">
|
||||
<span
|
||||
className={cn(textHover)}
|
||||
onMouseEnter={() => setHovered("text-1")}
|
||||
onMouseLeave={() => setHovered("")}
|
||||
>
|
||||
Creative
|
||||
</span>
|
||||
<div className="flex gap-4 justify-center items-center">
|
||||
<div
|
||||
className={
|
||||
"relative size-20 overflow-hidden rounded-full transition-all duration-150"
|
||||
}
|
||||
>
|
||||
<Image
|
||||
src={"/me.jpg"}
|
||||
alt="me"
|
||||
width={180}
|
||||
height={420}
|
||||
// style={{ objectPosition: "0 -1rem " }}
|
||||
className="object-cover group-hover:object-center transition-all -z-20 duration-300 absolute"
|
||||
/>
|
||||
{/* <div className="w-full h-60 -bottom-40 absolute hero-figure-gradient -z-10 group-hover:translate-y-40 transition-all duration-500 ease-in-out" /> */}
|
||||
</div>
|
||||
<>
|
||||
<div className="text-5xl lg:text-7xl rounded-md group font-bold max-w-xs lg:max-w-md mx-auto text-right flex flex-col relative cursor-default ">
|
||||
<span
|
||||
className={cn(textHover)}
|
||||
onMouseEnter={() => setHovered("text-2")}
|
||||
className={cn(
|
||||
`absolute left-4 lg:left-0 bottom-full z-50 ${textHover}`,
|
||||
hovered === "text-2" && "text-background blur-sm"
|
||||
)}
|
||||
onMouseEnter={() => setHovered("text-1")}
|
||||
onMouseLeave={() => setHovered("")}
|
||||
>
|
||||
Timeless
|
||||
</span>
|
||||
<div className="flex gap-4 justify-end items-center pr-4 lg:pr-0">
|
||||
<div
|
||||
className={cn(
|
||||
`size-12 rounded-full bg-background origin-right
|
||||
relative overflow-hidden transition-all duration-300 opacity-0 `,
|
||||
hovered === "text-2" && "scale-[300%] bg-primary opacity-100"
|
||||
)}
|
||||
>
|
||||
<Image src={"/hero.gif"} alt="herp-gif" fill />
|
||||
</div>
|
||||
<span
|
||||
className={textHover}
|
||||
onMouseEnter={() => setHovered("text-2")}
|
||||
onMouseLeave={() => setHovered("")}
|
||||
>
|
||||
Creative
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
onMouseEnter={() => setHovered("text-3")}
|
||||
onMouseLeave={() => setHovered("")}
|
||||
className={cn(
|
||||
`absolute left-4 lg:left-0 top-full ${textHover}`,
|
||||
hovered.length ? "text-foreground" : "text-primary",
|
||||
hovered === "text-2" && "text-background blur-sm"
|
||||
)}
|
||||
>
|
||||
Unique
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
onMouseEnter={() => setHovered("text-3")}
|
||||
onMouseLeave={() => setHovered("")}
|
||||
className={cn(
|
||||
`absolute left-0 top-full ${textHover}`,
|
||||
hovered.length ? "text-foreground" : "text-primary"
|
||||
)}
|
||||
>
|
||||
Unique
|
||||
</span>
|
||||
|
||||
<svg
|
||||
className="size-20 absolute top-2/3 -translate-y-2/3 transform -left-4 text-muted-foreground group-hover:text-popover-foreground transition-all duration-300"
|
||||
viewBox="0 0 284 201"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_102_3)">
|
||||
<path
|
||||
d="M95.2362 104.356C95.1418 97.0072 99.5149 92.501 104.083 88.8727C110.504 83.9101 117.525 79.5812 124.742 76.1301C158.402 61.1033 192.787 61.1147 227.086 73.8886C235.794 77.1673 244.177 81.7469 253.617 86.2445C254.06 76.4732 248.408 67.5999 248.315 57.4875C252.458 58.8669 253.643 62.134 254.698 64.8159C258.984 75.8362 263.155 87.0353 267.034 98.2996C268.852 103.729 267.73 106.282 262.464 107.456C251.527 110.047 240.867 109.046 230.778 104.386C230.42 104.159 230.226 103.281 229.788 101.997C236.648 95.5545 245.338 103.597 253.124 99.2512C242.615 88.5437 229.454 83.1866 215.968 79.1305C201.833 74.9121 187.55 72.1082 172.811 72.7839C158.364 73.3943 144.745 77.0445 131.093 81.9303C117.376 86.5234 105.722 94.1881 95.2362 104.356Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M6.29 130.2H27.232C33.5467 130.2 38.2087 131.557 41.218 134.27C44.2273 136.934 45.732 141.078 45.732 146.702C45.732 152.326 44.2273 156.495 41.218 159.208C38.2087 161.872 33.5467 163.204 27.232 163.204H16.724V182H6.29V130.2ZM27.528 155.064C29.7973 155.064 31.6473 154.373 33.078 152.992C34.558 151.611 35.298 149.514 35.298 146.702C35.298 143.89 34.558 141.793 33.078 140.412C31.6473 139.031 29.7973 138.34 27.528 138.34H16.724V155.064H27.528ZM80.2281 171.27C80.2281 172.602 80.4748 173.687 80.9681 174.526C81.5108 175.365 82.4481 176.277 83.7801 177.264L77.3421 182.888C74.5301 181.457 72.6061 179.657 71.5701 177.486C70.4848 179.015 69.0048 180.298 67.1301 181.334C65.2555 182.37 62.9368 182.888 60.1741 182.888C56.6715 182.888 53.8841 182 51.8121 180.224C49.7401 178.399 48.7041 175.833 48.7041 172.528C48.7041 169.568 49.4688 167.299 50.9981 165.72C52.5768 164.092 54.3775 162.982 56.4001 162.39C58.4228 161.798 60.6675 161.379 63.1341 161.132L66.6861 160.762C68.1168 160.614 69.1035 160.367 69.6461 160.022C70.2381 159.677 70.5588 159.109 70.6081 158.32C70.5095 156.84 70.0161 155.582 69.1281 154.546C68.2895 153.51 66.6615 152.992 64.2441 152.992C62.6655 152.992 61.2841 153.288 60.1001 153.88C58.9161 154.472 57.7075 155.434 56.4741 156.766L49.8881 152.178C51.3188 150.303 53.1195 148.873 55.2901 147.886C57.4608 146.85 60.4455 146.332 64.2441 146.332C74.9001 146.332 80.2281 150.649 80.2281 159.282V171.27ZM62.6161 176.006C65.1815 176.006 67.1548 175.389 68.5361 174.156C69.9175 172.873 70.6081 171.319 70.6081 169.494V166.386C69.8188 166.682 68.3141 166.953 66.0941 167.2L63.8741 167.422C62.1968 167.57 60.7908 167.965 59.6561 168.606C58.5215 169.198 57.9541 170.333 57.9541 172.01C57.9541 173.293 58.3735 174.279 59.2121 174.97C60.0508 175.661 61.1855 176.006 62.6161 176.006ZM110.086 182.888C105.449 182.888 101.872 181.457 99.356 178.596V182H89.736V138.118C89.736 136.786 89.4647 135.701 88.922 134.862C88.4287 134.023 87.516 133.111 86.184 132.124L92.622 126.5C95.138 127.783 96.8893 129.263 97.876 130.94C98.8627 132.568 99.356 134.862 99.356 137.822V150.846C101.921 147.837 105.498 146.332 110.086 146.332C113.342 146.332 116.105 147.121 118.374 148.7C120.693 150.229 122.419 152.301 123.554 154.916C124.689 157.481 125.256 160.318 125.256 163.426V165.794C125.256 168.902 124.689 171.763 123.554 174.378C122.419 176.943 120.693 179.015 118.374 180.594C116.105 182.123 113.342 182.888 110.086 182.888ZM107.274 175.488C109.987 175.488 112.059 174.551 113.49 172.676C114.921 170.801 115.636 168.507 115.636 165.794V163.426C115.636 160.713 114.921 158.419 113.49 156.544C112.059 154.669 109.987 153.732 107.274 153.732C103.13 153.732 100.491 155.755 99.356 159.8V169.42C100.491 173.465 103.13 175.488 107.274 175.488ZM139.613 182.888C137.097 181.605 135.345 180.15 134.359 178.522C133.372 176.845 132.879 174.526 132.879 171.566V138.118C132.879 136.786 132.607 135.701 132.065 134.862C131.571 134.023 130.659 133.111 129.327 132.124L135.765 126.5C138.281 127.783 140.032 129.263 141.019 130.94C142.005 132.568 142.499 134.862 142.499 137.822V171.27C142.499 172.602 142.745 173.687 143.239 174.526C143.781 175.365 144.719 176.277 146.051 177.264L139.613 182.888ZM167.242 182.888C162.111 182.888 158.164 181.704 155.402 179.336C152.639 176.968 151.258 173.589 151.258 169.198V160.022C151.258 155.631 152.639 152.252 155.402 149.884C158.164 147.516 162.111 146.332 167.242 146.332C172.372 146.332 176.319 147.516 179.082 149.884C181.844 152.252 183.226 155.631 183.226 160.022V169.198C183.226 173.589 181.844 176.968 179.082 179.336C176.319 181.704 172.372 182.888 167.242 182.888ZM167.242 175.488C169.264 175.488 170.818 174.945 171.904 173.86C173.038 172.775 173.606 171.221 173.606 169.198V160.022C173.606 157.999 173.038 156.445 171.904 155.36C170.818 154.275 169.264 153.732 167.242 153.732C165.219 153.732 163.64 154.275 162.506 155.36C161.42 156.445 160.878 157.999 160.878 160.022V169.198C160.878 171.221 161.42 172.775 162.506 173.86C163.64 174.945 165.219 175.488 167.242 175.488Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<defs>
|
||||
<clipPath id="clip0_102_3">
|
||||
<rect
|
||||
width="122"
|
||||
height="148"
|
||||
fill="currentColor"
|
||||
transform="translate(93 102.933) rotate(-57.5343)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Hero from "./hero";
|
||||
import Projects from "./projects";
|
||||
import CV from "./cv";
|
||||
import Tech from "./tech";
|
||||
|
||||
export { Hero, Projects, CV };
|
||||
export { Hero, Projects, Tech };
|
||||
|
||||
@ -14,7 +14,7 @@ const ProjectCard = (p: Project) => (
|
||||
>
|
||||
{/* <div className="font-serif text-muted-foreground w-20">{`[ ${p.year} ]`}</div> */}
|
||||
<div className="flex items-center gap-4 cursor-pointer hover:bg-muted w-full rounded-xl relative">
|
||||
<div className="h-40 w-32 relative rounded-xl overflow-hidden">
|
||||
<div className="h-40 w-full max-w-32 relative rounded-xl overflow-hidden">
|
||||
<Image
|
||||
src={p.image}
|
||||
alt="project-image"
|
||||
@ -22,10 +22,12 @@ const ProjectCard = (p: Project) => (
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-1 w-full">
|
||||
<span className="text-xs text-muted-foreground ">{`[ ${p.year} ]`}</span>
|
||||
<h3 className="text-3xl font-black">{p.name}</h3>
|
||||
<p className="text-muted-foreground text-lg">{p.description}</p>
|
||||
<h3 className="text-2xl md:text-3xl font-black">{p.name}</h3>
|
||||
<p className="text-muted-foreground text-sm md:text-lg">
|
||||
{p.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ArrowRight className="size-6 text-primary absolute right-4 top-8 -rotate-45 group-hover:-translate-y-4 group-hover:translate-x-2 transition-all duration-300" />
|
||||
@ -35,16 +37,20 @@ const ProjectCard = (p: Project) => (
|
||||
|
||||
function Projects() {
|
||||
return (
|
||||
<div className="flex gap-8 container lg:px-20">
|
||||
<div className="flex flex-col lg:flex-row gap-8 container lg:px-20">
|
||||
<MeCard
|
||||
className=" sticky top-20"
|
||||
className="mx-auto order-last lg:order-first lg:sticky lg:top-20"
|
||||
button={{
|
||||
label: "Contact Me",
|
||||
link: "/contact/#",
|
||||
link: "/contact",
|
||||
}}
|
||||
/>
|
||||
<div id="projects" className="space-y-8 w-full">
|
||||
<Heading title="Projects" subTitle="Recent" />
|
||||
<div className="space-y-8 w-full p-4">
|
||||
<Heading
|
||||
title="Projects"
|
||||
subTitle="Recent"
|
||||
// className="sticky top-0 z-50 pb-6"
|
||||
/>
|
||||
<menu className=" flex flex-col items-center gap-8 pb-20 w-full">
|
||||
{projects.map((p) => (
|
||||
<li key={p.link} className="w-full">
|
||||
|
||||
237
src/components/setions/tech.tsx
Normal file
237
src/components/setions/tech.tsx
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user