Proffesional freelance ui
This commit is contained in:
@@ -0,0 +1,379 @@
|
||||
import { Layout } from "@/components/layout/Layout";
|
||||
import {
|
||||
Github,
|
||||
Linkedin,
|
||||
Youtube,
|
||||
Instagram,
|
||||
ExternalLink,
|
||||
MapPin,
|
||||
Crown,
|
||||
Code,
|
||||
Trophy,
|
||||
Box,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
SiRust,
|
||||
SiTypescript,
|
||||
SiReact,
|
||||
SiGo,
|
||||
SiPython,
|
||||
SiDocker,
|
||||
SiLinux,
|
||||
} from "@icons-pack/react-simple-icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const timeline = [
|
||||
{
|
||||
year: "2020",
|
||||
title: "Started Programming Journey",
|
||||
description:
|
||||
"Began learning programming at age 10, with C# .net WinForms applications.",
|
||||
},
|
||||
{
|
||||
year: "2021",
|
||||
title: "Started learning Python and Web Development",
|
||||
description:
|
||||
"Began learning Python and web development, building my first fully coded applications.",
|
||||
},
|
||||
{
|
||||
year: "2022",
|
||||
title: "First Open Source Contributions",
|
||||
description:
|
||||
"Started contributing to open source projects and building my GitHub presence.",
|
||||
},
|
||||
{
|
||||
year: "2023",
|
||||
title: "Hacking tools",
|
||||
description:
|
||||
"Started building hacking tools and security-focused applications.",
|
||||
},
|
||||
{
|
||||
year: "2024",
|
||||
title: "Founded ORUS",
|
||||
description: "Created the orus-dev organization to house major projects.",
|
||||
},
|
||||
{
|
||||
year: "2025",
|
||||
title: "Expanding Horizons",
|
||||
description:
|
||||
"Taking on freelance work and building products that make a difference.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function About() {
|
||||
const techStack = [
|
||||
{ name: "Rust", icon: <SiRust />, color: "text-orange-500" },
|
||||
{ name: "TypeScript", icon: <SiTypescript />, color: "text-blue-500" },
|
||||
{ name: "React", icon: <SiReact />, color: "text-cyan-500" },
|
||||
{ name: "Go", icon: <SiGo />, color: "text-cyan-400" },
|
||||
{ name: "Python", icon: <SiPython />, color: "text-yellow-500" },
|
||||
{
|
||||
name: "Java",
|
||||
icon: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M8.717 18.555s-.922.561.641.721c1.882.24 2.885.2 4.969-.2a7.515 7.515 0 0 0 1.322.641c-4.688 2.004-10.62-.12-6.933-1.163zm-.6-2.605s-1.002.761.561.923c2.044.2 3.647.24 6.412-.32a2.445 2.445 0 0 0 .962.601c-5.651 1.683-11.982.16-7.935-1.202zm11.06 4.568s.681.561-.761 1.002c-2.685.803-11.261 1.043-13.665 0-.842-.36.761-.881 1.275-.961.521-.12.803-.12.803-.12-.922-.641-6.131 1.322-2.645 1.882 9.578 1.563 17.472-.681 14.987-1.803zM9.15 13.225s-4.368 1.043-1.563 1.402c1.202.161 3.566.12 5.771-.04 1.803-.16 3.608-.48 3.608-.48s-.641.281-1.082.561c-4.448 1.163-12.984.641-10.539-.561 2.084-1.002 3.807-.881 3.807-.881zm7.815 4.368c4.488-2.325 3.004-4.568.962-4.288-.36.08-.521.16-.521.16s.12-.24.401-.32c2.846-1.002 5.089 3.005-0.922 4.568 0 0 .04-.04.08-.12zm-7.373 6.332c4.328.28 10.94-.16 11.1-2.205 0 0-.32.803-3.566 1.402-3.687.681-8.255.6-10.94.16 0 0 .561.48 3.407.641z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M14.247.001s2.485 2.525-2.364 6.332c-3.887 3.086-.881 4.848 0 6.853-2.285-2.044-3.927-3.848-2.805-5.53C10.72 5.17 15.249 3.975 14.247.001zm-1.275 11.501c1.163 1.322-.32 2.525-.32 2.525s2.966-1.523 1.605-3.407c-1.242-1.803-2.205-2.685 3.005-5.69 0 0-8.215 2.044-4.288 6.572z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
color: "text-red-500",
|
||||
},
|
||||
{ name: "Docker", icon: <SiDocker />, color: "text-blue-400" },
|
||||
{ name: "Linux", icon: <SiLinux />, color: "text-yellow-400" },
|
||||
];
|
||||
|
||||
const achievements = [
|
||||
{
|
||||
icon: <Trophy className="w-10 h-10 text-accent mx-auto" />,
|
||||
title: "Top GitHub Contributor in Albania",
|
||||
description:
|
||||
"Ranked #2 nationally based on commits, contributions, and repository activity.",
|
||||
},
|
||||
{
|
||||
icon: <Github className="w-10 h-10 text-accent mx-auto" />,
|
||||
title: "Contributor to 80+ Public Repositories",
|
||||
description:
|
||||
"Actively contributed code, bug fixes, and features to multiple open-source projects.",
|
||||
},
|
||||
{
|
||||
icon: <Box className="w-10 h-10 text-accent mx-auto" />,
|
||||
title: "Built & Deployed 3 Full-Stack Applications",
|
||||
description:
|
||||
"Completed end-to-end development projects using Next.js, React, and TypeScript.",
|
||||
},
|
||||
{
|
||||
icon: <FileText className="w-10 h-10 text-accent mx-auto" />,
|
||||
title: "Published Technical Guides & Documentation",
|
||||
description:
|
||||
"Authored clear technical documentation and tutorials in public repositories.",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
{/* Hero */}
|
||||
<section className="pt-24 pb-16 bg-background relative overflow-hidden">
|
||||
<div className="absolute top-1/4 right-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl" />
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
|
||||
{/* Image/Avatar */}
|
||||
<div className="relative">
|
||||
<div className="aspect-square max-w-md mx-auto rounded-3xl overflow-hidden glass-card p-2">
|
||||
<div className="w-full h-full rounded-2xl bg-gradient-to-br from-accent/20 to-accent/5 flex items-center justify-center">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/103524696?v=4"
|
||||
alt="Klesti Selimaj"
|
||||
className="w-full h-full object-cover rounded-2xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Floating badges */}
|
||||
<div className="absolute -bottom-4 -right-4 glass-card rounded-xl px-4 py-2 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
|
||||
<span className="text-sm text-foreground">Available</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div>
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
About Me
|
||||
</span>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-foreground mt-2 mb-6">
|
||||
Hey, I'm{" "}
|
||||
<span className="text-gradient-gold">Klesti Selimaj</span>
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground mb-6">
|
||||
A 16-year-old software engineer from Albania with 6+ years of
|
||||
experience. I'm passionate about building things that matter,
|
||||
from low-level systems to beautiful user interfaces.
|
||||
</p>
|
||||
<p className="text-muted-foreground mb-8">
|
||||
My journey started at age 10 when I had my first laptop (a Asus
|
||||
with pre installed endless linux), and since then I've been
|
||||
hooked on computers, but i didn't exactly know what programming
|
||||
was but i was doing it unconsciously, making winforms apps in c#
|
||||
with visual studio.
|
||||
</p>
|
||||
|
||||
{/* Quick Info */}
|
||||
<div className="flex flex-wrap gap-4 mb-8">
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<MapPin className="w-4 h-4 text-accent" />
|
||||
<span>Albania</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Code className="w-4 h-4 text-accent" />
|
||||
<span>6+ Years Experience</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
<Crown className="w-4 h-4 text-accent" />
|
||||
<span>@orus-dev</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="flex items-center gap-4">
|
||||
<a
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
|
||||
>
|
||||
<Github className="w-5 h-5 text-foreground" />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
|
||||
>
|
||||
<Linkedin className="w-5 h-5 text-foreground" />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.youtube.com/@0xkleo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
|
||||
>
|
||||
<Youtube className="w-5 h-5 text-foreground" />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.instagram.com/selimaj.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
|
||||
>
|
||||
<Instagram className="w-5 h-5 text-foreground" />
|
||||
</a>
|
||||
<a
|
||||
href="https://dev.to/selimaj"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="glass-card p-3 rounded-xl hover:ring-1 hover:ring-accent/30 transition-all"
|
||||
>
|
||||
<ExternalLink className="w-5 h-5 text-foreground" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<section className="py-24 bg-card/50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Tech Stack
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
Tools I Work With
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
A carefully curated set of technologies that I use to build
|
||||
exceptional products.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{techStack.map((tech, index) => (
|
||||
<div
|
||||
key={tech.name}
|
||||
className="glass-card rounded-2xl p-6 text-center hover-lift group"
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
<div className={cn("text-4xl mb-3 mx-auto w-max", tech.color)}>
|
||||
{tech.icon}
|
||||
</div>
|
||||
<h3 className="font-semibold text-foreground group-hover:text-accent transition-colors">
|
||||
{tech.name}
|
||||
</h3>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Achievements */}
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Recognition
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
My Achievements
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Achievements earned through consistent contributions and learning.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{achievements.map((achievement, index) => (
|
||||
<div
|
||||
key={achievement.title}
|
||||
className="glass-card rounded-2xl p-6 text-center hover-lift"
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
<div className="text-4xl mb-4">{achievement.icon}</div>
|
||||
<h3 className="font-semibold text-foreground mb-2">
|
||||
{achievement.title}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{achievement.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Timeline */}
|
||||
<section className="py-24 bg-card/50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Journey
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
My Story
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
From curious kid to entrepreneurial developer, here's how I got
|
||||
here.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-3xl mx-auto">
|
||||
{timeline.map((item, index) => (
|
||||
<div
|
||||
key={item.year}
|
||||
className="relative flex gap-6 pb-12 last:pb-0"
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
{/* Line */}
|
||||
{index !== timeline.length - 1 && (
|
||||
<div className="absolute left-[19px] top-10 w-0.5 h-[calc(100%-2.5rem)] bg-border" />
|
||||
)}
|
||||
|
||||
{/* Dot */}
|
||||
<div className="relative flex-shrink-0">
|
||||
<div className="w-10 h-10 rounded-full bg-accent flex items-center justify-center">
|
||||
<span className="text-accent-foreground text-xs font-bold">
|
||||
{item.year.slice(2)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="glass-card rounded-2xl p-6 flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span className="text-sm font-mono text-accent">
|
||||
{item.year}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Philosophy */}
|
||||
<section className="py-24 bg-background relative overflow-hidden">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] bg-accent/5 rounded-full blur-3xl" />
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Philosophy
|
||||
</span>
|
||||
|
||||
<blockquote className="text-xl md:text-2xl text-foreground font-medium italic mb-6 mt-4">
|
||||
"One day or Day one?"
|
||||
</blockquote>
|
||||
<p className="text-muted-foreground">- Klesti Selimaj</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
+225
-29
@@ -1,41 +1,237 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Kodchasan Medium';
|
||||
src: url('/fonts/Kodchasan-Medium.ttf');
|
||||
}
|
||||
/* Premium Monochromatic Design System with Gold Accents */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Kodchasan';
|
||||
src: url('/fonts/Kodchasan-Regular.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Kodchasan Semi Bold';
|
||||
src: url('/fonts/Kodchasan-SemiBold.ttf');
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@layer base {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 5%;
|
||||
|
||||
--card: 0 0% 98%;
|
||||
--card-foreground: 0 0% 5%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 5%;
|
||||
|
||||
--primary: 0 0% 9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 0 0% 96%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
|
||||
--muted: 0 0% 94%;
|
||||
--muted-foreground: 0 0% 45%;
|
||||
|
||||
--accent: 43 41% 59%;
|
||||
--accent-foreground: 0 0% 5%;
|
||||
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--border: 0 0% 90%;
|
||||
--input: 0 0% 90%;
|
||||
--ring: 43 41% 59%;
|
||||
|
||||
--radius: 0.75rem;
|
||||
|
||||
/* Premium Gold Tokens */
|
||||
--gold: 43 41% 59%;
|
||||
--gold-light: 43 45% 70%;
|
||||
--gold-dark: 43 38% 45%;
|
||||
|
||||
/* Gradient tokens */
|
||||
--gradient-gold: linear-gradient(135deg, hsl(43 41% 59%), hsl(43 45% 70%));
|
||||
--gradient-dark: linear-gradient(180deg, hsl(0 0% 100%), hsl(0 0% 96%));
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.03);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -4px rgb(0 0 0 / 0.05);
|
||||
--shadow-gold: 0 0 30px -5px hsl(43 41% 59% / 0.15);
|
||||
|
||||
--sidebar-background: 0 0% 98%;
|
||||
--sidebar-foreground: 0 0% 25%;
|
||||
--sidebar-primary: 0 0% 10%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 0 0% 95%;
|
||||
--sidebar-accent-foreground: 0 0% 10%;
|
||||
--sidebar-border: 0 0% 91%;
|
||||
--sidebar-ring: 43 41% 59%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 0 0% 4%;
|
||||
--foreground: 0 0% 95%;
|
||||
|
||||
--card: 0 0% 7%;
|
||||
--card-foreground: 0 0% 95%;
|
||||
|
||||
--popover: 0 0% 6%;
|
||||
--popover-foreground: 0 0% 95%;
|
||||
|
||||
--primary: 0 0% 95%;
|
||||
--primary-foreground: 0 0% 5%;
|
||||
|
||||
--secondary: 0 0% 12%;
|
||||
--secondary-foreground: 0 0% 95%;
|
||||
|
||||
--muted: 0 0% 15%;
|
||||
--muted-foreground: 0 0% 55%;
|
||||
|
||||
--accent: 43 41% 59%;
|
||||
--accent-foreground: 0 0% 5%;
|
||||
|
||||
--destructive: 0 62% 30%;
|
||||
--destructive-foreground: 0 0% 95%;
|
||||
|
||||
--border: 0 0% 15%;
|
||||
--input: 0 0% 15%;
|
||||
--ring: 43 41% 59%;
|
||||
|
||||
/* Premium Gold Tokens (adjusted for dark) */
|
||||
--gold: 43 41% 59%;
|
||||
--gold-light: 43 50% 68%;
|
||||
--gold-dark: 43 35% 48%;
|
||||
|
||||
/* Gradient tokens */
|
||||
--gradient-gold: linear-gradient(135deg, hsl(43 41% 59%), hsl(43 50% 68%));
|
||||
--gradient-dark: linear-gradient(180deg, hsl(0 0% 4%), hsl(0 0% 8%));
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.5), 0 4px 6px -4px rgb(0 0 0 / 0.4);
|
||||
--shadow-gold: 0 0 40px -5px hsl(43 41% 59% / 0.25);
|
||||
|
||||
--sidebar-background: 0 0% 6%;
|
||||
--sidebar-foreground: 0 0% 90%;
|
||||
--sidebar-primary: 43 41% 59%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 0 0% 12%;
|
||||
--sidebar-accent-foreground: 0 0% 90%;
|
||||
--sidebar-border: 0 0% 12%;
|
||||
--sidebar-ring: 43 41% 59%;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: 'Kodchasan Semi Bold', sans-serif;
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground antialiased;
|
||||
font-family: 'Inter', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@apply bg-accent/30 text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
@layer components {
|
||||
.text-gradient-gold {
|
||||
@apply bg-clip-text text-transparent;
|
||||
background-image: linear-gradient(135deg, hsl(var(--gold)), hsl(var(--gold-light)));
|
||||
}
|
||||
|
||||
.border-gradient-gold {
|
||||
border-image: linear-gradient(135deg, hsl(var(--gold)), hsl(var(--gold-light))) 1;
|
||||
}
|
||||
|
||||
.glow-gold {
|
||||
box-shadow: 0 0 40px -5px hsl(var(--gold) / 0.3);
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
@apply bg-card/80 backdrop-blur-xl border border-border/50;
|
||||
}
|
||||
|
||||
.hover-lift {
|
||||
@apply transition-all duration-300 ease-out;
|
||||
}
|
||||
|
||||
.hover-lift:hover {
|
||||
@apply -translate-y-1;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.font-mono {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out forwards;
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fadeInUp 0.6s ease-out forwards;
|
||||
}
|
||||
|
||||
.animate-fade-in-delay-1 {
|
||||
animation: fadeIn 0.6s ease-out 0.1s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-fade-in-delay-2 {
|
||||
animation: fadeIn 0.6s ease-out 0.2s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-fade-in-delay-3 {
|
||||
animation: fadeIn 0.6s ease-out 0.3s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-shimmer {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
hsl(var(--gold) / 0) 0%,
|
||||
hsl(var(--gold) / 0.3) 50%,
|
||||
hsl(var(--gold) / 0) 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 3s infinite;
|
||||
}
|
||||
}
|
||||
+2
-8
@@ -1,10 +1,8 @@
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
import Navbar from "@/components/Navbar";
|
||||
import Background from "@/components/Background";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Klesti's Portfolio",
|
||||
title: "Selimaj.dev",
|
||||
description: "",
|
||||
};
|
||||
|
||||
@@ -15,11 +13,7 @@ export default function RootLayout({
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className="antialiased bg-black">
|
||||
<Background>
|
||||
<div className="relative h-screen">{children}</div>
|
||||
</Background>
|
||||
</body>
|
||||
<body className="antialiased bg-black">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
+12
-227
@@ -1,231 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { motion, spring } from "framer-motion";
|
||||
import PinnedRepos from "@/components/PinnedRepos";
|
||||
import Skill from "@/components/Skill";
|
||||
import {
|
||||
faGithub,
|
||||
faGolang,
|
||||
faInstagram,
|
||||
faJs,
|
||||
faLinkedin,
|
||||
faPython,
|
||||
faReact,
|
||||
faRust,
|
||||
} from "@fortawesome/free-brands-svg-icons";
|
||||
import { faJava } from "@fortawesome/free-brands-svg-icons/faJava";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { CoffeeIcon, HeartIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import GitHubCalendar from "react-github-calendar";
|
||||
import { fadeInUp, popIn, staggerContainer } from "@/components/effects";
|
||||
|
||||
export default function App() {
|
||||
const [contribs, setContribs] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchContributions = async () => {
|
||||
const res = await fetch("/api/contribs");
|
||||
const data = await res.json();
|
||||
setContribs(data.total || undefined);
|
||||
};
|
||||
fetchContributions();
|
||||
}, []);
|
||||
import { Layout } from "@/components/layout/Layout";
|
||||
import { HeroSection } from "@/components/home/HeroSection";
|
||||
import { ProjectsSection } from "@/components/home/ProjectsSection";
|
||||
import { SkillsSection } from "@/components/home/SkillsSection";
|
||||
import { CTASection } from "@/components/home/CTASection";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<motion.div
|
||||
className="w-full max-w-screen-lg mx-auto mt-10"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<div className="px-8 md:px-10 lg:px-15 xl:px-24">
|
||||
{/* HEADER */}
|
||||
<motion.h1
|
||||
className="py-2 text-gray-300"
|
||||
variants={fadeInUp}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
Hey, I'm Klesti! 👋🏻
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
className="font-semibold text-gray-300"
|
||||
variants={fadeInUp}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
transition={{ delay: 0.15 }}
|
||||
>
|
||||
A 16 year old Software Engineer from Tirane, Albania, with 6 years of
|
||||
experience, specializing in full-stack web development, desktop
|
||||
applications, TUI apps, and hacking. I also have a deep interest in
|
||||
low-level programming.
|
||||
</motion.p>
|
||||
|
||||
{/* KO-FI BUTTON */}
|
||||
<motion.div
|
||||
className="pt-6"
|
||||
animate={{ y: [0, -3, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 3 }}
|
||||
>
|
||||
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
|
||||
<motion.span
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="bg-transparent-ish rounded-md border border-slate-400 p-2 flex w-max gap-2 items-center cursor-pointer"
|
||||
>
|
||||
<CoffeeIcon /> Support me on Ko-fi
|
||||
</motion.span>
|
||||
</a>
|
||||
</motion.div>
|
||||
|
||||
{/* SOCIALS + SKILLS */}
|
||||
<motion.div
|
||||
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
|
||||
variants={staggerContainer}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
{/* socials */}
|
||||
<motion.div className="pt-5 flex flex-row gap-5 w-max">
|
||||
{[faGithub, faLinkedin, faInstagram].map((icon, i) => (
|
||||
<motion.a
|
||||
key={i}
|
||||
href={
|
||||
icon === faGithub
|
||||
? "https://github.com/selimaj-dev/"
|
||||
: icon === faLinkedin
|
||||
? "https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
: "https://www.instagram.com/selimaj.dev/"
|
||||
}
|
||||
target="blank_"
|
||||
variants={popIn}
|
||||
whileHover={{ scale: 1.2, rotate: 3 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className="text-slate-400 size-[2.3rem]"
|
||||
/>
|
||||
</motion.a>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* skills */}
|
||||
<motion.div variants={popIn}>
|
||||
<div className="p-3 rounded-xl">
|
||||
<motion.div
|
||||
className="mx-auto w-max flex flex-row gap-5"
|
||||
variants={staggerContainer}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="Rust" icon={faRust} />
|
||||
</motion.div>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="Go" icon={faGolang} />
|
||||
</motion.div>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="JavaScript" icon={faJs} />
|
||||
</motion.div>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="Java" icon={faJava} classInsert="p-1" />
|
||||
</motion.div>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="Python" icon={faPython} />
|
||||
</motion.div>
|
||||
<motion.div variants={popIn}>
|
||||
<Skill skill="React" icon={faReact} />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{/* PINNED PROJECTS */}
|
||||
<motion.h1
|
||||
className="text-slate-300 mt-4"
|
||||
variants={fadeInUp}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
Pinned Projects
|
||||
</motion.h1>
|
||||
|
||||
<motion.section>
|
||||
<PinnedRepos />
|
||||
</motion.section>
|
||||
|
||||
{/* CONTRIBUTIONS */}
|
||||
<motion.h2
|
||||
className="text-slate-300 mt-7"
|
||||
variants={fadeInUp}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
{contribs || "Loading.."} contributions in the last year
|
||||
</motion.h2>
|
||||
|
||||
<motion.section
|
||||
className="mt-4"
|
||||
variants={fadeInUp}
|
||||
initial="hidden"
|
||||
animate="show"
|
||||
>
|
||||
<motion.div variants={fadeInUp}>
|
||||
<GitHubCalendar
|
||||
// hideTotalCount
|
||||
username="selimaj-dev"
|
||||
theme={{
|
||||
light: [
|
||||
"rgba(255, 255, 255, 0.1)",
|
||||
"rgba(255, 255, 255, 0.3)",
|
||||
"rgba(255, 255, 255, 0.5)",
|
||||
"rgba(255, 255, 255, 0.7)",
|
||||
"rgba(255, 255, 255, 1)",
|
||||
],
|
||||
dark: [
|
||||
"rgba(255, 255, 255, 0.1)",
|
||||
"rgba(255, 255, 255, 0.3)",
|
||||
"rgba(255, 255, 255, 0.5)",
|
||||
"rgba(255, 255, 255, 0.7)",
|
||||
"rgba(255, 255, 255, 1)",
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.section>
|
||||
|
||||
{/* FOOTER */}
|
||||
<footer className="mt-8 border-t pt-3 pb-8">
|
||||
<motion.p
|
||||
className="flex gap-1 items-center"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
If you see this i{" "}
|
||||
<motion.span
|
||||
animate={{ scale: [1, 1.16, 1] }}
|
||||
transition={{ repeat: Infinity, duration: 1.0 }}
|
||||
>
|
||||
<HeartIcon className="size-4 text-slate-300" />
|
||||
</motion.span>{" "}
|
||||
you
|
||||
</motion.p>
|
||||
|
||||
<motion.p
|
||||
className="text-slate-300 mx-auto w-max mt-2"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.6 }}
|
||||
>
|
||||
© 2025 Klesti. All rights reserved.
|
||||
</motion.p>
|
||||
</footer>
|
||||
</div>
|
||||
</motion.div>
|
||||
<Layout>
|
||||
<HeroSection />
|
||||
<ProjectsSection />
|
||||
<SkillsSection />
|
||||
<CTASection />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
"use client";
|
||||
|
||||
import { Layout } from "@/components/layout/Layout";
|
||||
import { Check, ArrowRight, Zap, Crown, Rocket } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
const plans = [
|
||||
{
|
||||
name: "Starter",
|
||||
description: "Perfect for small projects and quick fixes",
|
||||
price: "$500",
|
||||
period: "project",
|
||||
icon: Zap,
|
||||
features: [
|
||||
"Single page application",
|
||||
"Responsive design",
|
||||
"Basic animations",
|
||||
"2 revision rounds",
|
||||
"1 week delivery",
|
||||
"Source code included",
|
||||
],
|
||||
cta: "Get Started",
|
||||
popular: false,
|
||||
},
|
||||
{
|
||||
name: "Professional",
|
||||
description: "For complex applications and systems",
|
||||
price: "$2,000",
|
||||
period: "project",
|
||||
icon: Crown,
|
||||
features: [
|
||||
"Multi-page application",
|
||||
"Custom backend integration",
|
||||
"Advanced animations & UI",
|
||||
"5 revision rounds",
|
||||
"2-3 weeks delivery",
|
||||
"Full source code & docs",
|
||||
"1 month support",
|
||||
"Performance optimization",
|
||||
],
|
||||
cta: "Start Project",
|
||||
popular: true,
|
||||
},
|
||||
{
|
||||
name: "Enterprise",
|
||||
description: "Full-scale systems and ongoing partnership",
|
||||
price: "Custom",
|
||||
period: "quote",
|
||||
icon: Rocket,
|
||||
features: [
|
||||
"Complex systems architecture",
|
||||
"Low-level development",
|
||||
"TUI/CLI applications",
|
||||
"Unlimited revisions",
|
||||
"Priority support",
|
||||
"Full documentation",
|
||||
"Training & onboarding",
|
||||
"Long-term partnership",
|
||||
],
|
||||
cta: "Contact Me",
|
||||
popular: false,
|
||||
},
|
||||
];
|
||||
|
||||
const services = [
|
||||
{
|
||||
title: "Web Development",
|
||||
description:
|
||||
"Modern, performant web applications built with React, TypeScript, and cutting-edge technologies.",
|
||||
},
|
||||
{
|
||||
title: "Systems Programming",
|
||||
description:
|
||||
"Low-level development in Rust and Go for performance-critical applications.",
|
||||
},
|
||||
{
|
||||
title: "TUI/CLI Tools",
|
||||
description:
|
||||
"Beautiful terminal user interfaces and command-line tools that developers love.",
|
||||
},
|
||||
{
|
||||
title: "API Development",
|
||||
description: "Robust, scalable backend systems and API architectures.",
|
||||
},
|
||||
{
|
||||
title: "DevOps & Deployment",
|
||||
description:
|
||||
"Docker, CI/CD pipelines, and self-hosted deployment solutions.",
|
||||
},
|
||||
{
|
||||
title: "Consulting",
|
||||
description:
|
||||
"Technical guidance and architecture planning for your projects.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Pricing() {
|
||||
const [hoveredPlan, setHoveredPlan] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
{/* Hero */}
|
||||
<section className="pt-24 pb-16 bg-background relative overflow-hidden">
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl" />
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Pricing
|
||||
</span>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-foreground mt-2 mb-6">
|
||||
Transparent Pricing for{" "}
|
||||
<span className="text-gradient-gold">Premium Quality</span>
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground">
|
||||
Choose a plan that fits your needs. Every project receives the
|
||||
same attention to detail and commitment to excellence.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pricing Cards */}
|
||||
<section className="py-16 bg-card/50">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-6xl mx-auto">
|
||||
{plans.map((plan) => (
|
||||
<div
|
||||
key={plan.name}
|
||||
className={`relative glass-card rounded-2xl p-8 transition-all duration-300 ${
|
||||
plan.popular
|
||||
? "ring-2 ring-accent scale-105 md:scale-110"
|
||||
: "hover:ring-1 hover:ring-accent/30"
|
||||
} ${hoveredPlan === plan.name ? "glow-gold" : ""}`}
|
||||
onMouseEnter={() => setHoveredPlan(plan.name)}
|
||||
onMouseLeave={() => setHoveredPlan(null)}
|
||||
>
|
||||
{plan.popular && (
|
||||
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
|
||||
<span className="bg-accent text-accent-foreground px-4 py-1 rounded-full text-sm font-medium">
|
||||
Most Popular
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-xl flex items-center justify-center ${
|
||||
plan.popular ? "bg-accent" : "bg-secondary"
|
||||
}`}
|
||||
>
|
||||
<plan.icon
|
||||
className={`w-5 h-5 ${
|
||||
plan.popular
|
||||
? "text-accent-foreground"
|
||||
: "text-foreground"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">
|
||||
{plan.name}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground text-sm mb-6">
|
||||
{plan.description}
|
||||
</p>
|
||||
|
||||
<div className="mb-6">
|
||||
<span className="text-4xl font-bold text-foreground">
|
||||
{plan.price}
|
||||
</span>
|
||||
{plan.period !== "quote" && (
|
||||
<span className="text-muted-foreground">
|
||||
/{plan.period}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ul className="space-y-3 mb-8">
|
||||
{plan.features.map((feature) => (
|
||||
<li
|
||||
key={feature}
|
||||
className="flex items-start gap-3 text-sm"
|
||||
>
|
||||
<Check className="w-4 h-4 text-accent mt-0.5 flex-shrink-0" />
|
||||
<span className="text-foreground">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<a
|
||||
href="mailto:[email protected]"
|
||||
className={`w-full flex items-center justify-center gap-2 py-3 rounded-xl font-medium transition-all ${
|
||||
plan.popular
|
||||
? "bg-accent text-accent-foreground hover:bg-accent/90"
|
||||
: "bg-secondary text-secondary-foreground hover:bg-secondary/80"
|
||||
}`}
|
||||
>
|
||||
{plan.cta}
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Services */}
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-6">
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Services
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
What I Offer
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
From concept to deployment, I provide end-to-end development
|
||||
services tailored to your needs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{services.map((service, index) => (
|
||||
<div
|
||||
key={service.title}
|
||||
className="glass-card rounded-2xl p-6 hover-lift group"
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-2 group-hover:text-accent transition-colors">
|
||||
{service.title}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{service.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Contact CTA */}
|
||||
<section className="py-24 bg-card/50 relative overflow-hidden">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-accent/5 rounded-full blur-3xl" />
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-6">
|
||||
Have a Custom Project in Mind?
|
||||
</h2>
|
||||
<p className="text-lg text-muted-foreground mb-10 max-w-xl mx-auto">
|
||||
Let's discuss your requirements and create a tailored solution
|
||||
that exceeds expectations.
|
||||
</p>
|
||||
<a
|
||||
href="mailto:[email protected]"
|
||||
className="inline-flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all glow-gold"
|
||||
>
|
||||
Schedule a Call
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
import { Client } from "@notionhq/client";
|
||||
import { SearchResponse } from "@notionhq/client/build/src/api-endpoints";
|
||||
import { NotionToMarkdown } from "notion-to-md";
|
||||
|
||||
interface Blog {
|
||||
id: string;
|
||||
title: string;
|
||||
tags: { name: string; color: string }[];
|
||||
date: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
type QueryResponse = { cursor: string | null; hasMore: boolean; items: Blog[] };
|
||||
type Error = { msg: string };
|
||||
|
||||
export async function getBlogs(
|
||||
notion: Client,
|
||||
databaseId: string,
|
||||
cursor: string | undefined,
|
||||
pageSize: number
|
||||
): Promise<[number, QueryResponse | Error]> {
|
||||
try {
|
||||
const response = await notion.databases.query({
|
||||
database_id: databaseId,
|
||||
filter: {
|
||||
property: "Status",
|
||||
status: {
|
||||
equals: "Posted",
|
||||
},
|
||||
},
|
||||
start_cursor: cursor,
|
||||
page_size: pageSize,
|
||||
});
|
||||
return [
|
||||
200,
|
||||
{
|
||||
cursor: response.next_cursor,
|
||||
hasMore: response.has_more,
|
||||
items: response.results.map(convertBlog),
|
||||
},
|
||||
];
|
||||
} catch (err: any) {
|
||||
return [400, { msg: String(err) }];
|
||||
}
|
||||
}
|
||||
|
||||
function convertBlog(page: any): Blog {
|
||||
return {
|
||||
id: page.id,
|
||||
title: page.properties.Title.title[0].plain_text,
|
||||
description: [
|
||||
page.properties.Description.rich_text.map(
|
||||
(d: { plain_text: string }) => d.plain_text
|
||||
),
|
||||
].join(" "),
|
||||
date: page.properties.Date.created_time,
|
||||
tags: page.properties.Tags.multi_select.map((tag: any) => ({
|
||||
name: tag.name,
|
||||
color: tag.color,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
interface FullBlog extends Blog {
|
||||
markdown: string;
|
||||
}
|
||||
|
||||
export async function getBlog(
|
||||
notion: Client,
|
||||
n2m: NotionToMarkdown,
|
||||
pageId: string
|
||||
): Promise<[number, FullBlog | Error]> {
|
||||
try {
|
||||
const page: any = await notion.pages.retrieve({ page_id: pageId });
|
||||
if (page.properties.Status.status.name !== "Posted")
|
||||
return [400, { msg: "Blog not found" }];
|
||||
try {
|
||||
const blog = convertBlog(page);
|
||||
const mdBlocks = await n2m.pageToMarkdown(page.id);
|
||||
return [
|
||||
200,
|
||||
{
|
||||
id: blog.id,
|
||||
title: blog.title,
|
||||
description: blog.description,
|
||||
tags: blog.tags,
|
||||
date: blog.date,
|
||||
markdown: n2m.toMarkdownString(mdBlocks).parent || "",
|
||||
},
|
||||
];
|
||||
} catch (err: any) {
|
||||
return [500, { msg: String(err) }];
|
||||
}
|
||||
} catch {
|
||||
return [400, { msg: "Blog not found" }];
|
||||
}
|
||||
}
|
||||
|
||||
export async function searchBlog(
|
||||
notion: Client,
|
||||
query: string
|
||||
): Promise<[200 | 400, QueryResponse | Error]> {
|
||||
try {
|
||||
var response = await notion.search({
|
||||
query,
|
||||
filter: {
|
||||
value: "page",
|
||||
property: "object",
|
||||
},
|
||||
sort: {
|
||||
direction: "ascending",
|
||||
timestamp: "last_edited_time",
|
||||
},
|
||||
});
|
||||
response.results = response.results.filter(
|
||||
(item: any) => item.properties.Status.status.name === "Posted"
|
||||
);
|
||||
return [
|
||||
200,
|
||||
{
|
||||
cursor: response.next_cursor,
|
||||
hasMore: response.has_more,
|
||||
items: response.results.map(convertBlog),
|
||||
},
|
||||
];
|
||||
} catch (err: any) {
|
||||
return [400, { msg: String(err) }];
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function Background({
|
||||
children,
|
||||
}: Readonly<{ children: React.ReactNode }>) {
|
||||
return (
|
||||
<div className="relative w-full min-h-screen overflow-hidden bg-black">
|
||||
<div
|
||||
className="absolute inset-0 bg-black"
|
||||
style={{
|
||||
backgroundImage:
|
||||
"radial-gradient(circle, rgba(100, 116, 139, 0.5) 1px, transparent 1px)",
|
||||
backgroundSize: "20px 20px",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="relative z-50 overflow-y-scroll overflow-x-hidden">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { gruvboxDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import "katex/dist/katex.min.css";
|
||||
|
||||
type MarkdownRendererProps = {
|
||||
children: string;
|
||||
};
|
||||
|
||||
export default function MarkdownRenderer({
|
||||
children: markdown,
|
||||
}: MarkdownRendererProps) {
|
||||
return (
|
||||
<Markdown
|
||||
remarkPlugins={[remarkGfm, remarkMath, remarkBreaks]}
|
||||
rehypePlugins={[rehypeRaw, rehypeKatex]}
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }: any) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
return !inline && match ? (
|
||||
<SyntaxHighlighter
|
||||
style={gruvboxDark}
|
||||
PreTag="div"
|
||||
language={match[1]}
|
||||
{...props}
|
||||
>
|
||||
{String(children).replace(/\n$/, "")}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
ul({ children, ...props }) {
|
||||
return (
|
||||
<ul
|
||||
style={{ listStyleType: "disc", listStylePosition: "inside" }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
},
|
||||
ol({ children, ...props }) {
|
||||
return (
|
||||
<ol
|
||||
style={{ listStyleType: "decimal", listStylePosition: "inside" }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
},
|
||||
li({ children, ...props }) {
|
||||
return (
|
||||
<li style={{ marginBottom: "4px" }} {...props}>
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{markdown}
|
||||
</Markdown>
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Navbar() {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<nav className="py-4 flex">
|
||||
<div className="mx-auto flex">
|
||||
<div className="space-x-4 text-white block font-bold">
|
||||
<Link
|
||||
href="/"
|
||||
className={
|
||||
"hover:slate-200 transition-colors duration-300" +
|
||||
(pathname === "/" ? " slate-200" : "")
|
||||
}
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className={
|
||||
"hover:slate-200 transition-colors duration-300" +
|
||||
(pathname === "/pricing" ? " slate-200" : "")
|
||||
}
|
||||
>
|
||||
Pricing
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { fadeInUp, fadeInUp2, staggerContainer } from "./effects";
|
||||
|
||||
type PinnedRepo = {
|
||||
author: string;
|
||||
name: string;
|
||||
description: string;
|
||||
language: string;
|
||||
stars: number;
|
||||
forks: number;
|
||||
href?: string;
|
||||
};
|
||||
|
||||
export function Repo({
|
||||
name,
|
||||
description: desc,
|
||||
language: lang,
|
||||
stars,
|
||||
forks,
|
||||
href,
|
||||
}: PinnedRepo) {
|
||||
return (
|
||||
<motion.div
|
||||
variants={fadeInUp2}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
className="group relative flex size-full overflow-hidden rounded-xl h-full border border-slate-400 hover:shadow-md transition-shadow duration-300 backdrop-blur-[2px]"
|
||||
>
|
||||
<div className="w-full">
|
||||
<div className="rounded-xl border text-card-foreground shadow h-full bg-transparent border-none flex flex-col">
|
||||
<div className="flex flex-col space-y-1.5 p-6 flex-grow">
|
||||
<h3 className="tracking-tight text-base sm:text-lg font-semibold text-slate-300">
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="hover:underline"
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
</h3>
|
||||
<p className="text-xs sm:text-sm text-gray-300 line-clamp-2 mt-1">
|
||||
{desc}
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 pt-0 mt-auto">
|
||||
<div className="flex flex-wrap justify-between items-center gap-2">
|
||||
<div className="flex items-center gap-2 sm:gap-4">
|
||||
<div
|
||||
className="flex items-center gap-1 text-xs sm:text-sm"
|
||||
data-state="closed"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="lucide lucide-star text-gray-300 h-3 w-3 sm:h-4 sm:w-4"
|
||||
>
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" />
|
||||
</svg>
|
||||
<span className="text-gray-300">{stars}</span>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-1 text-xs sm:text-sm"
|
||||
data-state="closed"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="lucide lucide-git-fork text-gray-500 h-3 w-3 sm:h-4 sm:w-4"
|
||||
>
|
||||
<circle cx="12" cy="18" r="3" />
|
||||
<circle cx="6" cy="6" r="3" />
|
||||
<circle cx="18" cy="6" r="3" />
|
||||
<path d="M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9" />
|
||||
<path d="M12 12v3" />
|
||||
</svg>
|
||||
<span className="text-gray-300">{forks}</span>
|
||||
</div>
|
||||
</div>
|
||||
{lang && (
|
||||
<div
|
||||
className="inline-flex items-center rounded-md border px-2.5 py-0.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-foreground text-xs bg-slate-400/10 border-slate-400"
|
||||
data-sentry-component="Badge"
|
||||
data-sentry-source-file="badge.tsx"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="lucide lucide-code-xml h-3 w-3 mr-1"
|
||||
>
|
||||
<path d="m18 16 4-4-4-4" />
|
||||
<path d="m6 8-4 4 4 4" />
|
||||
<path d="m14.5 4-5 16" />
|
||||
</svg>
|
||||
{lang}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PinnedRepos() {
|
||||
try {
|
||||
const [pinned, setPinned] = useState<PinnedRepo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("https://pinned.berrysauce.dev/get/selimaj-dev")
|
||||
.then((r) => {
|
||||
r.json()
|
||||
.then((p) => {
|
||||
setPinned(p);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
variants={staggerContainer}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
|
||||
>
|
||||
{pinned &&
|
||||
pinned.map((repo, i) => (
|
||||
<Repo
|
||||
key={i}
|
||||
author={repo.author}
|
||||
name={repo.name}
|
||||
description={repo.description}
|
||||
language={repo.language}
|
||||
stars={repo.stars}
|
||||
forks={repo.forks}
|
||||
href={`https://github.com/${repo.author}/${repo.name}`}
|
||||
/>
|
||||
))}
|
||||
</motion.div>
|
||||
);
|
||||
} catch (e: any) {
|
||||
return <p>{e.toString()}</p>;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function Skill({
|
||||
skill,
|
||||
icon,
|
||||
classInsert,
|
||||
}: {
|
||||
skill: string;
|
||||
icon: IconProp;
|
||||
classInsert?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative flex items-center justify-center group">
|
||||
<span
|
||||
className="absolute bottom-full mb-2 left-1/2 -translate-x-1/2
|
||||
bg-black text-white text-sm px-2 py-1 rounded
|
||||
opacity-0 group-hover:opacity-100 transition-opacity duration-300 whitespace-nowrap"
|
||||
>
|
||||
Skilled in {skill}
|
||||
</span>
|
||||
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={`text-slate-400 ${classInsert} size-[2.3rem]`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { easeOut, spring } from "framer-motion";
|
||||
|
||||
export const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
show: { opacity: 1, y: 0, transition: { duration: 0.5 } },
|
||||
};
|
||||
|
||||
export const fadeInUp2 = {
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.4, ease: easeOut },
|
||||
},
|
||||
};
|
||||
|
||||
export const staggerContainer = {
|
||||
show: {
|
||||
transition: {
|
||||
staggerChildren: 0.12,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const popIn = {
|
||||
hidden: { opacity: 0, scale: 0.8 },
|
||||
show: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: { type: spring, stiffness: 120 },
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export function CTASection() {
|
||||
return (
|
||||
<section className="py-24 bg-card/50 relative overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-accent/5 rounded-full blur-3xl" />
|
||||
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-3xl md:text-5xl font-bold text-foreground mb-6">
|
||||
Ready to build something{" "}
|
||||
<span className="text-gradient-gold">extraordinary</span>?
|
||||
</h2>
|
||||
<p className="text-lg text-muted-foreground mb-10 max-w-xl mx-auto">
|
||||
Whether you need a complex system, a modern web application, or specialized tooling,
|
||||
I'm here to bring your vision to life.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="group flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all hover:gap-3 glow-gold"
|
||||
>
|
||||
View Pricing Plans
|
||||
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
|
||||
</Link>
|
||||
<Link
|
||||
href="/about"
|
||||
className="flex items-center gap-2 text-foreground hover:text-accent font-medium transition-colors"
|
||||
>
|
||||
Learn more about me
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { ArrowRight, Github, Linkedin, Youtube } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export function HeroSection() {
|
||||
return (
|
||||
<section className="relative min-h-[90vh] flex items-center justify-center overflow-hidden">
|
||||
{/* Background Effects */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-background via-background to-card" />
|
||||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-accent/5 rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-accent/10 rounded-full blur-3xl" />
|
||||
|
||||
<div className="container mx-auto px-6 relative z-10">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
{/* Status Badge */}
|
||||
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full glass-card mb-8 animate-fade-in">
|
||||
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Available for new projects
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Main Heading */}
|
||||
<h1 className="text-4xl md:text-6xl lg:text-7xl font-bold mb-6 animate-fade-in-up">
|
||||
<span className="text-foreground">Building</span>{" "}
|
||||
<span className="text-gradient-gold">exceptional</span>
|
||||
<br />
|
||||
<span className="text-foreground">digital experiences</span>
|
||||
</h1>
|
||||
|
||||
{/* Subheading */}
|
||||
<p className="text-lg md:text-xl text-muted-foreground max-w-2xl mx-auto mb-10 animate-fade-in-delay-1">
|
||||
I'm{" "}
|
||||
<span className="text-foreground font-medium">Klesti Selimaj</span>,
|
||||
a 16-year-old software engineer with 6+ years of experience.
|
||||
Passionate about low-level systems, modern web apps, and crafting
|
||||
tools that make a difference.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-12 animate-fade-in-delay-2">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="group flex items-center gap-2 bg-accent text-accent-foreground px-8 py-4 rounded-xl font-medium hover:bg-accent/90 transition-all hover:gap-3"
|
||||
>
|
||||
Work with me
|
||||
<ArrowRight className="w-4 h-4 transition-transform group-hover:translate-x-1" />
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 bg-secondary text-secondary-foreground px-8 py-4 rounded-xl font-medium hover:bg-secondary/80 transition-colors"
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
View GitHub
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Social Links */}
|
||||
<div className="flex items-center justify-center gap-6 animate-fade-in-delay-3">
|
||||
<a
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
<Github className="w-5 h-5" />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
<Linkedin className="w-5 h-5" />
|
||||
</a>
|
||||
<a
|
||||
href="https://www.youtube.com/@0xkleo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
<Youtube className="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 animate-bounce">
|
||||
<div className="w-6 h-10 border-2 border-muted-foreground/30 rounded-full flex items-start justify-center p-2">
|
||||
<div className="w-1 h-2 bg-accent rounded-full animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import { ExternalLink, Github, Star } from "lucide-react";
|
||||
|
||||
const projects = [
|
||||
{
|
||||
name: "DockShip",
|
||||
description: "A powerful self-hosted Deployment Platform for modern teams.",
|
||||
tech: ["TypeScript", "Docker", "React"],
|
||||
stars: 1,
|
||||
github: "https://github.com/orus-dev/dockship",
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
name: "OSUI",
|
||||
description:
|
||||
"A TUI library written in Rust for building beautiful terminal interfaces.",
|
||||
tech: ["Rust", "TUI", "Systems"],
|
||||
stars: 8,
|
||||
github: "https://github.com/osui-rs/osui",
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
name: "Linear Linux",
|
||||
description:
|
||||
"Linux support for linear.app - bringing seamless project management to Linux users.",
|
||||
tech: ["JavaScript", "Electron", "Linux"],
|
||||
stars: 23,
|
||||
github: "https://github.com/selimaj-dev/linear-linux",
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
name: "Saturn Client",
|
||||
description:
|
||||
"A Minecraft client full of customizability and free cosmetics.",
|
||||
tech: ["Java", "Minecraft", "Gaming"],
|
||||
stars: 3,
|
||||
github: "https://github.com/saturnclientmc/saturnclient",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
name: "Axiom Chat",
|
||||
description: "A modern, feature-rich chat application built for teams.",
|
||||
tech: ["TypeScript", "Real-time", "WebSockets"],
|
||||
stars: 1,
|
||||
github: "https://github.com/AxiomChat/axiom",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
name: "Drift",
|
||||
description: "An organized productivity app to get rid of distractions.",
|
||||
tech: ["TypeScript", "Productivity", "Focus"],
|
||||
stars: 1,
|
||||
github: "https://github.com/orus-dev/drift",
|
||||
featured: false,
|
||||
},
|
||||
];
|
||||
|
||||
export function ProjectsSection() {
|
||||
return (
|
||||
<section className="py-24 bg-card/50">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Section Header */}
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Portfolio
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
Featured Projects
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
A selection of my open-source work and personal projects that
|
||||
showcase my skills in systems programming, web development, and
|
||||
tooling.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Projects Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{projects.map((project, index) => (
|
||||
<article
|
||||
key={project.name}
|
||||
className={"group glass-card rounded-2xl p-6 hover-lift"}
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
{project.featured && (
|
||||
<span className="px-2 py-1 text-xs font-medium bg-accent/10 text-accent rounded-full">
|
||||
Featured
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 text-muted-foreground">
|
||||
<Star className="w-4 h-4" />
|
||||
<span className="text-sm">{project.stars}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<h3 className="text-xl font-semibold text-foreground mb-2 group-hover:text-accent transition-colors">
|
||||
{project.name}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm mb-4 line-clamp-2">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
{/* Tech Stack */}
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{project.tech.map((tech) => (
|
||||
<span
|
||||
key={tech}
|
||||
className="px-2 py-1 text-xs font-mono bg-secondary text-secondary-foreground rounded"
|
||||
>
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
<Github className="w-4 h-4" />
|
||||
Source
|
||||
</a>
|
||||
<a
|
||||
href={project.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
View
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* View All CTA */}
|
||||
<div className="text-center mt-12">
|
||||
<a
|
||||
href="https://github.com/selimaj-dev?tab=repositories"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 text-accent hover:text-accent/80 font-medium transition-colors"
|
||||
>
|
||||
View all projects on GitHub
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
const skills = [
|
||||
{
|
||||
category: "Languages",
|
||||
items: [
|
||||
{ name: "Rust", level: 90 },
|
||||
{ name: "TypeScript", level: 95 },
|
||||
{ name: "JavaScript", level: 95 },
|
||||
{ name: "Python", level: 80 },
|
||||
{ name: "Go", level: 75 },
|
||||
{ name: "Java", level: 85 },
|
||||
],
|
||||
},
|
||||
{
|
||||
category: "Frontend",
|
||||
items: [
|
||||
{ name: "React", level: 95 },
|
||||
{ name: "Next.js", level: 85 },
|
||||
{ name: "Tailwind CSS", level: 95 },
|
||||
{ name: "Framer Motion", level: 80 },
|
||||
],
|
||||
},
|
||||
{
|
||||
category: "Systems & Tools",
|
||||
items: [
|
||||
{ name: "Linux", level: 90 },
|
||||
{ name: "Docker", level: 85 },
|
||||
{ name: "Git", level: 95 },
|
||||
{ name: "TUI Development", level: 90 },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const stats = [
|
||||
{ label: "Years Experience", value: "6+" },
|
||||
{ label: "GitHub Followers", value: "32" },
|
||||
{ label: "Open Source Projects", value: "10+" },
|
||||
{ label: "Technologies Mastered", value: "15+" },
|
||||
];
|
||||
|
||||
export function SkillsSection() {
|
||||
return (
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 mb-20">
|
||||
{stats.map((stat, index) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="text-center p-6 glass-card rounded-2xl hover-lift"
|
||||
style={{ animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
<div className="text-3xl md:text-4xl font-bold text-gradient-gold mb-2">
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Section Header */}
|
||||
<div className="max-w-2xl mb-16">
|
||||
<span className="text-accent text-sm font-medium uppercase tracking-wider">
|
||||
Expertise
|
||||
</span>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-foreground mt-2 mb-4">
|
||||
Technical Skills
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
From low-level systems programming to modern web development, I
|
||||
bring a diverse skill set to every project.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Skills Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{skills.map((category, categoryIndex) => (
|
||||
<div
|
||||
key={category.category}
|
||||
className="glass-card rounded-2xl p-6"
|
||||
style={{ animationDelay: `${categoryIndex * 150}ms` }}
|
||||
>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-6 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-accent" />
|
||||
{category.category}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{category.items.map((skill) => (
|
||||
<div key={skill.name}>
|
||||
<div className="flex justify-between text-sm mb-2">
|
||||
<span className="text-foreground font-medium">
|
||||
{skill.name}
|
||||
</span>
|
||||
<span className="text-muted-foreground">
|
||||
{skill.level}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1.5 bg-secondary rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-accent rounded-full transition-all duration-1000 ease-out"
|
||||
style={{ width: `${skill.level}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export function Footer() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="border-t border-border bg-card/50">
|
||||
<div className="container mx-auto px-6 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
|
||||
{/* Brand */}
|
||||
<div className="md:col-span-2">
|
||||
<Link href="/" className="flex items-center gap-2 mb-4">
|
||||
<img
|
||||
className="w-8 h-8 rounded-lg bg-accent flex items-center justify-center"
|
||||
src="/logo.png"
|
||||
alt="Logo"
|
||||
/>
|
||||
<span className="font-semibold text-foreground">selimaj.dev</span>
|
||||
</Link>
|
||||
<p className="text-muted-foreground text-sm max-w-sm">
|
||||
Building digital experiences with precision and passion.
|
||||
Specializing in low-level systems, modern web applications, and
|
||||
everything in between.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div>
|
||||
<h4 className="font-medium text-foreground mb-4">Quick Links</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/about"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
About
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
Hire Me
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Connect */}
|
||||
<div>
|
||||
<h4 className="font-medium text-foreground mb-4">Connect</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
LinkedIn
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.youtube.com/@0xkleo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
YouTube
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.instagram.com/selimaj.dev/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-muted-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
Instagram
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border mt-8 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
© {currentYear} Klesti Selimaj. All rights reserved.
|
||||
</p>
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<span className="w-2 h-2 rounded-full bg-accent animate-pulse" />
|
||||
Available for work
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
"use client";
|
||||
|
||||
import { ReactNode } from "react";
|
||||
import { Footer } from "./Footer";
|
||||
import { Navbar } from "./Navbar";
|
||||
|
||||
interface LayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export function Layout({ children }: LayoutProps) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background dark">
|
||||
<Navbar />
|
||||
<main className="flex-1 pt-16">{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CoffeeIcon, Github, Menu, X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const navLinks = [
|
||||
{ href: "/", label: "Home" },
|
||||
{ href: "/about", label: "About" },
|
||||
{ href: "/pricing", label: "Hire Me" },
|
||||
];
|
||||
|
||||
export function Navbar() {
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<header className="fixed top-0 left-0 right-0 z-50 glass-card border-b">
|
||||
<nav className="container mx-auto px-6 h-16 flex items-center justify-between">
|
||||
<Link href="/" className="flex items-center gap-2 group">
|
||||
<img
|
||||
className="w-8 h-8 rounded-lg bg-accent flex items-center justify-center"
|
||||
src="/logo.png"
|
||||
alt="Logo"
|
||||
/>
|
||||
<span className="font-semibold text-foreground group-hover:text-accent transition-colors">
|
||||
selimaj.dev
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Desktop Nav */}
|
||||
<ul className="hidden md:flex items-center gap-8">
|
||||
{navLinks.map((link) => {
|
||||
const isActive = pathname === link.href;
|
||||
|
||||
return (
|
||||
<li key={link.href}>
|
||||
<Link
|
||||
href={link.href}
|
||||
className={cn(
|
||||
"text-sm font-medium transition-colors hover:text-accent relative py-2",
|
||||
isActive ? "text-accent" : "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{link.label}
|
||||
{isActive && (
|
||||
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-accent rounded-full" />
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
<div className="hidden md:flex items-center gap-4">
|
||||
<motion.li
|
||||
animate={{ y: [0, -3, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 3 }}
|
||||
>
|
||||
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
|
||||
<motion.span
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<CoffeeIcon />
|
||||
</motion.span>
|
||||
</a>
|
||||
</motion.li>
|
||||
<a
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Github />
|
||||
</a>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="bg-accent text-accent-foreground px-4 py-2 rounded-lg text-sm font-medium hover:bg-accent/90 transition-colors"
|
||||
>
|
||||
Get in Touch
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
className="md:hidden text-foreground p-2"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<X className="w-5 h-5" />
|
||||
) : (
|
||||
<Menu className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden glass-card border-t animate-fade-in">
|
||||
<ul className="container mx-auto px-6 py-4 space-y-4">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<Link
|
||||
href={link.href}
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className={cn(
|
||||
"block text-sm font-medium transition-colors",
|
||||
location.pathname === link.href
|
||||
? "text-accent"
|
||||
: "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<Link
|
||||
href="https://github.com/selimaj-dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Github />
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<motion.li
|
||||
animate={{ y: [0, -3, 0] }}
|
||||
transition={{ repeat: Infinity, duration: 3 }}
|
||||
>
|
||||
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
|
||||
<motion.span
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<CoffeeIcon />
|
||||
</motion.span>
|
||||
</a>
|
||||
</motion.li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import dotenv from 'dotenv';
|
||||
import { Client } from '@notionhq/client';
|
||||
import { NotionToMarkdown } from 'notion-to-md';
|
||||
dotenv.config();
|
||||
|
||||
export const notion = new Client( { auth: process.env.NOTION_API_KEY } )
|
||||
export const n2m = new NotionToMarkdown({
|
||||
notionClient: notion,
|
||||
});
|
||||
|
||||
if (process.env.NOTION_DATABASE_ID === undefined) {
|
||||
console.error('[server] Error: environment variable \'NOTION_DATABASE_ID\' not found')
|
||||
process.exit();
|
||||
}
|
||||
export const databaseId: string = process.env.NOTION_DATABASE_ID;
|
||||
Reference in New Issue
Block a user