-
-
-
-
}>
-
- {(resolvedPosts: Tables<"posts">[]) => (
-
- )}
-
-
-
-
-
+
+
+
}>
+
+ {(resolvedPosts) => }
+
+
+
+
);
}
diff --git a/app/routes/contact.tsx b/app/routes/contact.tsx
new file mode 100644
index 0000000..3bc96dc
--- /dev/null
+++ b/app/routes/contact.tsx
@@ -0,0 +1,101 @@
+import { SEOHandle } from "@nasa-gcn/remix-seo";
+import { MetaFunction } from "@remix-run/node";
+
+import { Avatar } from "~/components/atoms/Avatar";
+import { SocialMedia } from "~/components/molecules/SocialMedia";
+import { Footer } from "~/components/organisms/Footer";
+import { Header } from "~/components/organisms/Header";
+
+export const handle: SEOHandle = {
+ /**
+ * Asynchronously retrieve sitemap.xml entries for the route
+ *
+ * @returns The sitemap.xml entries for the route
+ */
+ getSitemapEntries: async () => {
+ return [{ route: "/contact", priority: 0.7, changefreq: "monthly" }];
+ },
+};
+
+/**
+ * Generate metadata for the route
+ *
+ * @returns The meta tags for the route
+ */
+export const meta: MetaFunction = () => {
+ return [
+ { title: "Contact" },
+ { description: "Gleb Khaykin's contact information" },
+ {
+ property: "og:title",
+ content: "Contact",
+ },
+ {
+ property: "og:description",
+ content: "Gleb Khaykin's contact information",
+ },
+ {
+ property: "og:type",
+ content: "website",
+ },
+ {
+ property: "og:url",
+ content: "https://khaykingleb.com/contact",
+ },
+ {
+ property: "og:image",
+ content: "avatar.webp",
+ },
+ ];
+};
+
+/**
+ * The main component for the route
+ *
+ * @returns The route layout
+ */
+export default function ContactRoute() {
+ return (
+
+
+
+
+
+
+
+
About
+
+
+ -
+ I'm a full-stack developer specializing in MLOps/DevOps
+ engineering
+
+ -
+ When I'm not coding, you'll find me at the gym,
+ lifting weights 🏋️♀️
+
+ -
+ I studied both Computer Science and Finance at the Higher
+ School of Economics (this dual background helps me
+ understand both business and technical aspects of the
+ projects)
+
+
+
+
Links
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/styles/notion.css b/app/styles/notion.css
index 1f1693b..42aea56 100644
--- a/app/styles/notion.css
+++ b/app/styles/notion.css
@@ -19,14 +19,20 @@
}
.notion {
+ color: hsl(var(--bc)) !important;
+ background: hsl(var(--b1)) !important;
+
--notion-max-width: 100%;
}
-.notion-title,
.notion-asset-caption {
@apply font-gill-sans text-center;
}
+.notion-table-of-contents {
+ @apply m-0 -mt-10 p-0 pt-0 !important;
+}
+
.notion-table-of-contents,
.notion-h1,
.notion-h2,
@@ -39,6 +45,8 @@
}
.notion-title {
+ @apply hidden !important;
+
margin-top: -1.5rem !important;
font-size: 1.75rem !important;
}
diff --git a/app/styles/tailwind.css b/app/styles/tailwind.css
index 2d341a8..3469df5 100644
--- a/app/styles/tailwind.css
+++ b/app/styles/tailwind.css
@@ -1,3 +1,5 @@
+@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Raleway:ital,wght@0,100..900;1,100..900&family=Reddit+Sans:ital,wght@0,200..900;1,200..900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
+
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -6,4 +8,8 @@
.font-gill-sans {
font-family: "Gill Sans", sans-serif;
}
+
+ .font-poppins {
+ font-family: Poppins, sans-serif;
+ }
}
diff --git a/app/utils/theme.ts b/app/utils/theme.ts
new file mode 100644
index 0000000..d916e54
--- /dev/null
+++ b/app/utils/theme.ts
@@ -0,0 +1,39 @@
+import { useEffect, useState } from "react";
+
+export type Theme = "light" | "dark";
+export const THEME_COOKIE = "theme";
+
+export const getInitialTheme = (): Theme => {
+ if (typeof window === "undefined") return "light";
+ return (localStorage.getItem("theme") as Theme) || "light";
+};
+
+export const setTheme = (theme: Theme) => {
+ localStorage.setItem("theme", theme);
+ document.cookie = `${THEME_COOKIE}=${theme}; path=/; max-age=${60 * 60 * 24 * 365}`;
+ if (theme === "dark") {
+ document.documentElement.classList.add("dark");
+ document.documentElement.setAttribute("data-theme", "dark");
+ } else {
+ document.documentElement.classList.remove("dark");
+ document.documentElement.setAttribute("data-theme", "light");
+ }
+};
+
+/**
+ * Custom hook to manage theme
+ *
+ * @returns Theme and setTheme function
+ */
+export function useTheme() {
+ const [theme, setCurrentTheme] = useState
(getInitialTheme());
+
+ useEffect(() => {
+ setTheme(theme);
+ }, [theme]);
+
+ return {
+ theme,
+ setTheme: setCurrentTheme,
+ };
+}
diff --git a/cspell.config.js b/cspell.config.js
index e6972eb..837b522 100644
--- a/cspell.config.js
+++ b/cspell.config.js
@@ -13,6 +13,7 @@ export default {
"pnpm-lock.yaml",
"supabase",
"docker-compose.yaml",
+ "app/components/molecules/AsciiDonut.tsx",
],
words: [
"workdir",
diff --git a/public/Gleb_Khaykin.pdf b/public/Gleb_Khaykin.pdf
index 8bc3052..06dd21c 100644
Binary files a/public/Gleb_Khaykin.pdf and b/public/Gleb_Khaykin.pdf differ
diff --git a/supabase/migrations/20250108051612_add_comments_posts.sql b/supabase/migrations/20250108051612_add_comments_posts.sql
new file mode 100644
index 0000000..b55a770
--- /dev/null
+++ b/supabase/migrations/20250108051612_add_comments_posts.sql
@@ -0,0 +1,12 @@
+-- Add table description
+comment on table public.posts is 'Table storing blog posts from Notion';
+
+-- Add column descriptions
+comment on column public.posts.title is 'Title of the blog post';
+comment on column public.posts.slug is
+'URL-friendly version of the title used for routing';
+comment on column public.posts.notion_page_id is
+'Associated Notion page identifier';
+comment on column public.posts.tags is 'Array of tags associated with the post';
+comment on column public.posts.image_url is
+'URL of the featured image for the post';
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 7049982..7835d21 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -5,7 +5,26 @@ import type { Config } from "tailwindcss";
export default {
content: ["./app/**/*.{ts,tsx,jsx,js}"],
plugins: [daisyUI],
+ darkMode: "class",
+ theme: {
+ extend: {
+ fontFamily: {
+ sans: ["Gill Sans", "sans-serif"],
+ },
+ },
+ },
daisyui: {
- darkTheme: false, // disable dark theme
+ themes: [
+ {
+ light: {
+ "base-100": "#ffffff",
+ "base-content": "#000000",
+ },
+ dark: {
+ "base-100": "#000000",
+ "base-content": "#ffffff",
+ },
+ },
+ ],
},
} satisfies Config;