Updated layout and background
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import { getBlog, getBlogs } from "@/blogApi";
|
||||
import { notion, databaseId, n2m } from "@/notion";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
const id = url.searchParams.get("id");
|
||||
if (id) {
|
||||
let [status, data] = await getBlog(notion, n2m, id);
|
||||
return Response.json(data, { status });
|
||||
} else {
|
||||
var cursor = url.searchParams.get("cursor") || undefined;
|
||||
if (cursor === "") cursor = undefined;
|
||||
const size = parseInt(url.searchParams.get("size") || "5");
|
||||
let [status, response] = await getBlogs(notion, databaseId, cursor, size);
|
||||
return Response.json(response, { status });
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export function GET(req: Request) {
|
||||
return new Response(JSON.stringify({
|
||||
version: "1.0.2",
|
||||
}));
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { getBlogs, searchBlog } from "@/blogApi";
|
||||
import { notion, databaseId } from "@/notion";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url);
|
||||
let [status, response] = await searchBlog(
|
||||
notion,
|
||||
url.searchParams.get("query") || ""
|
||||
);
|
||||
return Response.json(response, { status });
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import MarkdownRenderer from "@/components/MarkdownRenderer";
|
||||
import { useEffect, useState } from "react";
|
||||
import "./style.css";
|
||||
|
||||
type Blog = {
|
||||
title: string;
|
||||
description: string;
|
||||
id: string;
|
||||
tags: { name: string; color: string }[];
|
||||
markdown: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
export default function Blog({ params }: { params: Promise<{ id: string }> }) {
|
||||
const [blog, setBlog] = useState<null | Blog>(null);
|
||||
|
||||
useEffect(() => {
|
||||
params.then(({ id }) =>
|
||||
fetch(`/api/blog/?id=${id}`)
|
||||
.then((res) => {
|
||||
res
|
||||
.json()
|
||||
.then((response) => {
|
||||
if (res.status === 200) {
|
||||
setBlog(response);
|
||||
} else {
|
||||
console.error(`'/api/blog/${id}' Error:`, response.msg);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`'/api/blog/${id}' Unable to obtain json:`, e);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(`'/api/blog/${id}' Error:`, e);
|
||||
})
|
||||
);
|
||||
}, [params]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="content-center w-full lg:mx-auto pt-16 lg:pt-0 relative z-20 pb-16 md:pb-0 animate-fadeIn">
|
||||
<div className="w-full max-w-screen-lg mx-auto">
|
||||
<div className="px-6 md:px-10 lg:px-15 xl:px-2">
|
||||
<div className="pt-8 pb-6 px-8 bg-transparent-ish rounded-xl h-screen">
|
||||
{blog ? (
|
||||
<div>
|
||||
<h1 className="text-red-400">{blog.title}</h1>
|
||||
<p className="text-gray-400">
|
||||
{new Date(blog.date).toDateString()}
|
||||
</p>
|
||||
<div className="mt-4 blog-md">
|
||||
<MarkdownRenderer>
|
||||
{blog.markdown
|
||||
.replaceAll(/\n!\n/g, "\n<br/>\n")
|
||||
.replaceAll(/\n-/g, "\n\n-")}
|
||||
</MarkdownRenderer>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.blog-md h1 {
|
||||
color: #fa5770;
|
||||
}
|
||||
|
||||
.blog-md a {
|
||||
color: #fa5770;
|
||||
}
|
||||
|
||||
.blog-md code {
|
||||
color: #fa5770;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Blog = {
|
||||
title: string;
|
||||
description: string;
|
||||
id: string;
|
||||
tags: { name: string; color: string }[];
|
||||
};
|
||||
|
||||
export default function Blog() {
|
||||
const [blogs, setBlogs] = useState<Blog[]>([]);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const fetchBlogs = (query: string) => {
|
||||
(query === ""
|
||||
? fetch("/api/blog/")
|
||||
: fetch(`/api/search/?query=${encodeURIComponent(query)}`)
|
||||
)
|
||||
.then((res) => {
|
||||
res
|
||||
.json()
|
||||
.then((response) => {
|
||||
if (response && response.items) {
|
||||
setBlogs(response.items);
|
||||
} else {
|
||||
console.error("`/api/blog/` Response is invalid:", response);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("`/api/blog/` Unable to obtain json:", e);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error("`/api/blog/` Error:", e);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
fetchBlogs(searchQuery);
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(handler);
|
||||
}, [searchQuery]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="content-center w-full lg:mx-auto pt-16 lg:pt-0 relative z-20 pb-16 md:pb-0 animate-fadeIn">
|
||||
<div className="w-full max-w-screen-lg mx-auto">
|
||||
<div className="px-6 md:px-10 lg:px-15 xl:px-2">
|
||||
<div className="pt-8 pb-6 px-8 bg-transparent-ish rounded-xl h-screen">
|
||||
<h1 className="text-red-400 w-max mx-auto">Leo's blog</h1>
|
||||
<input
|
||||
type="search"
|
||||
name="Blog"
|
||||
id="blog-search"
|
||||
className="p-3 bg-transparent border border-red rounded-xl w-72 mx-auto mt-5"
|
||||
onChange={(event) => setSearchQuery(event.target.value)}
|
||||
placeholder="Search"
|
||||
/>
|
||||
<div className="mt-10 grid gap-6 w-full lg:grid-cols-2">
|
||||
{blogs.map((blog, i) => (
|
||||
<div
|
||||
className="p-3 px-5 h-52 border border-red rounded-xl backdrop-blur-xl"
|
||||
key={i}
|
||||
>
|
||||
<h1 className="hover:text-red text-ellipsis overflow-hidden">
|
||||
<Link href={"/blog/" + blog.id}>{blog.title}</Link>
|
||||
</h1>
|
||||
<p className="pt-1 max-h-[72px] overflow-hidden">
|
||||
{blog.description}
|
||||
</p>
|
||||
<div className="flex gap-5 pt-3 flex-wrap overflow-hidden h-10">
|
||||
{blog.tags.map((tag, i) => (
|
||||
<span
|
||||
className="inline-flex items-center rounded-md border px-2.5 py-0.5 font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 text-foreground text-xs bg-red-600/10 border-red-600"
|
||||
key={i}
|
||||
>
|
||||
{tag.name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -38,13 +38,4 @@ body {
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.skill .tooltip {
|
||||
opacity: 0;
|
||||
transition: opacity .3s linear;
|
||||
}
|
||||
|
||||
.skill:hover .tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
+3
-3
@@ -16,9 +16,9 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`antialiased bg-black`}>
|
||||
<Background />
|
||||
<Navbar />
|
||||
{children}
|
||||
<Background>
|
||||
{children}
|
||||
</Background>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
+39
-45
@@ -10,76 +10,70 @@ import {
|
||||
faReact,
|
||||
faRust,
|
||||
} from "@fortawesome/free-brands-svg-icons";
|
||||
import { faJava } from "@fortawesome/free-brands-svg-icons/faJava";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div>
|
||||
<div className="content-center w-full lg:mx-auto pt-16 lg:pt-0 relative z-20 pb-16 md:pb-0 animate-fadeIn">
|
||||
<div className="content-center w-full lg:mx-auto pt-5 relative z-20 pb-10 animate-fadeIn">
|
||||
<div className="w-full max-w-screen-lg mx-auto">
|
||||
<div className="px-8 md:px-10 lg:px-15 xl:px-24">
|
||||
<h1 className="py-2 text-gray-300">Hey, I'm Leo 👋🏻</h1>
|
||||
<h1 className="py-2 text-gray-300">Hey, I'm Klesti! 👋🏻</h1>
|
||||
<p className="font-semibold text-gray-300">
|
||||
A 15 year old Software Engineer from Tirane, Albania, with 5 years
|
||||
of experience, specializing in full-stack web development, desktop
|
||||
applications, TUI apps, and hacking. I have a deep interest in
|
||||
low-level programming.
|
||||
</p>
|
||||
<div className="pt-5 flex flex-row gap-5 w-max">
|
||||
<a href="https://github.com/kleo-dev/" target="blank_">
|
||||
<FontAwesomeIcon
|
||||
icon={faGithub}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
target="blank_"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faLinkedin}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/kleo.dev/" target="blank_">
|
||||
<FontAwesomeIcon
|
||||
icon={faInstagram}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<h1 className="pt-5 text-red-400">Pinned Projects</h1>
|
||||
<section className="">
|
||||
<PinnedRepos />
|
||||
</section>
|
||||
|
||||
{/* Experience and skill */}
|
||||
{/* Socials and skill */}
|
||||
<div className="pt-10 grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent">
|
||||
{/* skill */}
|
||||
{/* socials */}
|
||||
<div className="pt-5 flex flex-row gap-5 w-max">
|
||||
<a href="https://github.com/kleo-dev/" target="blank_">
|
||||
<FontAwesomeIcon
|
||||
icon={faGithub}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
|
||||
target="blank_"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faLinkedin}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/kleo.dev/" target="blank_">
|
||||
<FontAwesomeIcon
|
||||
icon={faInstagram}
|
||||
width={36}
|
||||
className="text-red"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="pb-3 text-red-400 w-max">My Skills</h1>
|
||||
<div className="p-3 bg-transparent-ish rounded-xl">
|
||||
<div className="mx-auto w-max flex flex-row gap-5">
|
||||
<Skill skill="Rust" icon={faRust} />
|
||||
<Skill skill="Python" icon={faPython} />
|
||||
<Skill skill="Go" icon={faGolang} mt="mt-2" />
|
||||
<Skill skill="Go" icon={faGolang} />
|
||||
<Skill skill="JavaScript" icon={faJs} />
|
||||
<Skill skill="Java" icon={faJava} classInsert="p-1" />
|
||||
<Skill skill="Python" icon={faPython} />
|
||||
<Skill skill="React" icon={faReact} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Experience */}
|
||||
<div>
|
||||
<h1 className="pb-3 text-red-400 w-max">My Experience</h1>
|
||||
<div className="p-3 bg-transparent-ish rounded-xl">
|
||||
<div className="mx-auto w-max">Soon</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="pt-5 text-red-400">Pinned Projects</h1>
|
||||
<section className="">
|
||||
<PinnedRepos />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
import {
|
||||
faCheck,
|
||||
faCode,
|
||||
faDatabase,
|
||||
faRocket,
|
||||
faWrench,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
export default function Pricing() {
|
||||
return (
|
||||
<div>
|
||||
{/* <Background /> */}
|
||||
<div className="content-center w-full lg:mx-auto pt-16 lg:pt-0 relative z-20 pb-16 md:pb-0 animate-fadeIn">
|
||||
<div className="w-full max-w-screen-lg mx-auto px-4 md:px-10">
|
||||
<h1 className="pt-10 w-max mx-auto text-red-400">Pricing plans</h1>
|
||||
<div className="pt-10 h-full grid grid-cols-1 lg:grid-cols-2 grid-rows-2 gap-8">
|
||||
<div className="h-full rounded-xl bg-transparent-ish border border-red p-9">
|
||||
<FontAwesomeIcon
|
||||
className="mx-auto text-red-600 w-6"
|
||||
icon={faCode}
|
||||
/>
|
||||
<h1 className="w-max mx-auto text-red-400">Standard</h1>
|
||||
<h2 className="text-2xl w-max mx-auto text-red">$25</h2>
|
||||
<ul className="pt-4 list-none space-y-2">
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
24-48 hours
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Simple projects or tasks (e.g., bug fixes)
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Response via Email or Discord
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="h-full rounded-xl bg-transparent-ish border border-red p-9">
|
||||
<FontAwesomeIcon
|
||||
className="mx-auto text-red-600 w-6"
|
||||
icon={faRocket}
|
||||
/>
|
||||
<h1 className="w-max mx-auto text-red-400">Premium</h1>
|
||||
<h2 className="text-2xl w-max mx-auto text-red">$50</h2>
|
||||
<ul className="pt-4 list-none space-y-2">
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Up to a week
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Complex projects
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Large tasks (e.g., refractoring, bug hunting)
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Documenting
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="h-full rounded-xl bg-transparent-ish border border-red p-9">
|
||||
<FontAwesomeIcon
|
||||
className="mx-auto text-red-600 w-6"
|
||||
icon={faDatabase}
|
||||
/>
|
||||
<h1 className="w-max mx-auto text-red-400">Enterprise</h1>
|
||||
<h2 className="text-2xl w-max mx-auto text-red">$200</h2>
|
||||
<ul className="pt-4 list-none space-y-2">
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Up to a month
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Large, Complex projects
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Higher priority
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="h-full rounded-xl bg-transparent-ish border border-red p-9">
|
||||
<FontAwesomeIcon
|
||||
className="mx-auto text-red-600 w-6"
|
||||
icon={faWrench}
|
||||
/>
|
||||
<h1 className="w-max mx-auto text-red-400">Maintenance</h1>
|
||||
<h2 className="text-2xl w-max mx-auto text-red">$40-150 / mo</h2>
|
||||
<ul className="pt-4 list-none space-y-2">
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Weekly bug checks & fixes
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Cybersecurity pen testing
|
||||
</li>
|
||||
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Feature requests
|
||||
</li>
|
||||
|
||||
<li className="flex items-center">
|
||||
<FontAwesomeIcon
|
||||
className="text-green-500 mr-2"
|
||||
icon={faCheck}
|
||||
width={14}
|
||||
/>
|
||||
Performance Optimization
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p className="md:mx-auto md:w-max">NOTE: if the project requires more than a month there will be a extra payments</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user