mirror of
https://github.com/Xevion/xevion.dev.git
synced 2026-01-31 12:26:39 -06:00
chore: migrate from Tailwind v3 to v4 and upgrade dependencies
Major changes: - Upgrade Tailwind CSS from v3 to v4 with @tailwindcss/postcss - Remove deprecated Tailwind v3 configuration and PostCSS plugins - Upgrade Zod from v3 to v4 - Update React Markdown and other dependencies to latest versions - Remove @plaiceholder, @headlessui, and other unused dependencies - Replace SCSS with standard CSS globals - Add font packages (@fontsource-variable) for better typography - Consolidate Prettier configuration to .prettierrc - Remove legacy contact page and custom Payload SCSS - Add Mantine hooks and Lucide React for improved UI components
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
+1
-6
@@ -12,12 +12,7 @@ const compat = new FlatCompat({
|
|||||||
const eslintConfig = [
|
const eslintConfig = [
|
||||||
...compat.extends("next/core-web-vitals"),
|
...compat.extends("next/core-web-vitals"),
|
||||||
{
|
{
|
||||||
ignores: [
|
ignores: [".next/**", "out/**", "build/**", "next-env.d.ts"],
|
||||||
".next/**",
|
|
||||||
"out/**",
|
|
||||||
"build/**",
|
|
||||||
"next-env.d.ts",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -1,8 +1,6 @@
|
|||||||
import { withPayload } from "@payloadcms/next/withPayload";
|
import { withPayload } from "@payloadcms/next/withPayload";
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
import withPlaiceholder from "@plaiceholder/next";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
|
||||||
* This is especially useful for Docker builds.
|
* This is especially useful for Docker builds.
|
||||||
@@ -70,6 +68,9 @@ const config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
turbopack: {
|
||||||
|
resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.json'],
|
||||||
|
},
|
||||||
async redirects() {
|
async redirects() {
|
||||||
// Source cannot end with / slash
|
// Source cannot end with / slash
|
||||||
return [
|
return [
|
||||||
@@ -92,4 +93,4 @@ const config = {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export default withPayload(withPlaiceholder(config));
|
export default withPayload(config);
|
||||||
|
|||||||
+19
-28
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "xevion.dev",
|
"name": "xevion.dev",
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -11,55 +10,47 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.27.16",
|
"@fontsource-variable/inter": "^5.1.0",
|
||||||
"@headlessui/react": "^2.2.0",
|
"@fontsource-variable/roboto": "^5.1.0",
|
||||||
"@kodingdotninja/use-tailwind-breakpoint": "^1.0.0",
|
"@fontsource-variable/roboto-mono": "^5.1.0",
|
||||||
|
"@fontsource/hanken-grotesk": "^5.1.0",
|
||||||
|
"@mantine/hooks": "^8",
|
||||||
"@next/eslint-plugin-next": "^15.1.1",
|
"@next/eslint-plugin-next": "^15.1.1",
|
||||||
"@octokit/core": "^6.1.2",
|
"@octokit/core": "^7.0.5",
|
||||||
"@payloadcms/db-postgres": "^3.61.1",
|
"@payloadcms/db-postgres": "^3.61.1",
|
||||||
"@payloadcms/next": "^3.61.1",
|
"@payloadcms/next": "^3.61.1",
|
||||||
"@payloadcms/payload-cloud": "^3.61.1",
|
"@payloadcms/payload-cloud": "^3.61.1",
|
||||||
"@payloadcms/richtext-lexical": "^3.61.1",
|
"@payloadcms/richtext-lexical": "^3.61.1",
|
||||||
"@plaiceholder/next": "^3.0.0",
|
|
||||||
"@tailwindcss/typography": "^0.5.8",
|
|
||||||
"@tanstack/react-query": "^5.90",
|
"@tanstack/react-query": "^5.90",
|
||||||
"@vercel/analytics": "^1.5.0",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.1.1",
|
||||||
"graphql": "^16.11.0",
|
"graphql": "^16.11.0",
|
||||||
|
"lucide-react": "^0.548.0",
|
||||||
"next": "^15.5.6",
|
"next": "^15.5.6",
|
||||||
"p5i": "^0.6.0",
|
"p5i": "^0.6.0",
|
||||||
"payload": "^3.61.1",
|
"payload": "^3.61.1",
|
||||||
"plaiceholder": "^3.0.0",
|
|
||||||
"prettier": "^3.4.2",
|
|
||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-icons": "^4.10.1",
|
"react-markdown": "^10.1.0",
|
||||||
"react-markdown": "^9.0.1",
|
|
||||||
"react-wrap-balancer": "^1",
|
"react-wrap-balancer": "^1",
|
||||||
"sass": "^1.56.2",
|
|
||||||
"sharp": "^0.34",
|
"sharp": "^0.34",
|
||||||
"superjson": "^2.2",
|
"superjson": "^2.2",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^3.3.1",
|
||||||
"usehooks-ts": "^3.1.1",
|
"zod": "^4.1.12"
|
||||||
"zod": "^3.24.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.38.0",
|
"@eslint/js": "^9.38.0",
|
||||||
"@types/node": "^20",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/react": "^19",
|
"@types/node": "^24.9.1",
|
||||||
"@types/react-dom": "^19",
|
"@types/react": "^19.2.2",
|
||||||
"autoprefixer": "^10.4.7",
|
"@types/react-dom": "^19.2.2",
|
||||||
"eslint": "^9.17.0",
|
"eslint": "^9.38.0",
|
||||||
"eslint-config-next": "^15.1.1",
|
"eslint-config-next": "^15.1.1",
|
||||||
"postcss": "^8",
|
"prettier": "^3.6.2",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.9",
|
"tailwindcss": "^4",
|
||||||
"tailwindcss": "^3.4.1",
|
"typescript": "^5.7.2"
|
||||||
"typescript": "^5"
|
|
||||||
},
|
|
||||||
"ct3aMetadata": {
|
|
||||||
"initVersion": "6.11.3"
|
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf"
|
"packageManager": "pnpm@9.15.1+sha512.1acb565e6193efbebda772702950469150cf12bcc764262e7587e71d19dc98a423dff9536e57ea44c49bdf790ff694e83c27be5faa23d67e0c033b583be4bfcf"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+7234
-4642
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
|||||||
module.exports = {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
"@tailwindcss/postcss": {},
|
||||||
autoprefixer: {},
|
|
||||||
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
|
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/** @type {import("prettier").Config} */
|
|
||||||
module.exports = {
|
|
||||||
plugins: [require.resolve("prettier-plugin-tailwindcss")],
|
|
||||||
};
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import AppWrapper from "@/components/AppWrapper";
|
|
||||||
import { BsDiscord, BsGithub } from "react-icons/bs";
|
|
||||||
import { AiFillMail } from "react-icons/ai";
|
|
||||||
import Link from "next/link";
|
|
||||||
import type { IconType } from "react-icons";
|
|
||||||
import {
|
|
||||||
useFloating,
|
|
||||||
autoUpdate,
|
|
||||||
offset,
|
|
||||||
flip,
|
|
||||||
shift,
|
|
||||||
useHover,
|
|
||||||
useFocus,
|
|
||||||
useDismiss,
|
|
||||||
useRole,
|
|
||||||
useInteractions,
|
|
||||||
FloatingPortal,
|
|
||||||
} from "@floating-ui/react";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const socials: {
|
|
||||||
icon: IconType;
|
|
||||||
href?: string;
|
|
||||||
hint?: string;
|
|
||||||
hideHint?: boolean;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
icon: BsGithub,
|
|
||||||
href: "https://github.com/Xevion/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: AiFillMail,
|
|
||||||
href: "mailto:xevion@xevion.dev",
|
|
||||||
hint: "xevion@xevion.dev",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: BsDiscord,
|
|
||||||
hint: "Xevion#8506",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function SocialTooltip({
|
|
||||||
icon: Icon,
|
|
||||||
href,
|
|
||||||
hint,
|
|
||||||
hideHint,
|
|
||||||
}: {
|
|
||||||
icon: IconType;
|
|
||||||
href?: string;
|
|
||||||
hint?: string;
|
|
||||||
hideHint?: boolean;
|
|
||||||
}) {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const { refs, floatingStyles, context } = useFloating({
|
|
||||||
open: isOpen,
|
|
||||||
onOpenChange: setIsOpen,
|
|
||||||
placement: "top",
|
|
||||||
whileElementsMounted: autoUpdate,
|
|
||||||
middleware: [offset(10), flip(), shift()],
|
|
||||||
});
|
|
||||||
|
|
||||||
const hover = useHover(context);
|
|
||||||
const focus = useFocus(context);
|
|
||||||
const dismiss = useDismiss(context);
|
|
||||||
const role = useRole(context, { role: "tooltip" });
|
|
||||||
|
|
||||||
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
||||||
hover,
|
|
||||||
focus,
|
|
||||||
dismiss,
|
|
||||||
role,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const inner = <Icon className="h-8 w-8" />;
|
|
||||||
const tooltipContent = hint ?? href;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{href != undefined ? (
|
|
||||||
<Link
|
|
||||||
href={href}
|
|
||||||
ref={refs.setReference}
|
|
||||||
{...getReferenceProps()}
|
|
||||||
>
|
|
||||||
{inner}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span
|
|
||||||
ref={refs.setReference}
|
|
||||||
{...getReferenceProps()}
|
|
||||||
>
|
|
||||||
{inner}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{!hideHint && isOpen && tooltipContent && (
|
|
||||||
<FloatingPortal>
|
|
||||||
<div
|
|
||||||
ref={refs.setFloating}
|
|
||||||
style={floatingStyles}
|
|
||||||
{...getFloatingProps()}
|
|
||||||
className="z-50 rounded bg-zinc-900 px-3 py-2 text-sm text-zinc-100 shadow-lg"
|
|
||||||
>
|
|
||||||
{tooltipContent}
|
|
||||||
</div>
|
|
||||||
</FloatingPortal>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ContactPage() {
|
|
||||||
return (
|
|
||||||
<AppWrapper>
|
|
||||||
<div className="my-10 flex w-full flex-col items-center">
|
|
||||||
<div className="mx-3 flex w-full max-w-[23rem] flex-col rounded-md border border-zinc-800 bg-zinc-800/50 p-5 sm:max-w-[25rem] lg:max-w-[30rem]">
|
|
||||||
<div className="flex justify-center gap-x-5 text-center">
|
|
||||||
{socials.map((social, index) => (
|
|
||||||
<SocialTooltip key={index} {...social} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AppWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "@/styles/globals.scss";
|
|
||||||
|
// Fontsource imports
|
||||||
|
import "@fontsource-variable/inter";
|
||||||
|
import "@fontsource-variable/roboto";
|
||||||
|
import "@fontsource-variable/roboto-mono";
|
||||||
|
import "@fontsource/hanken-grotesk/900.css";
|
||||||
|
|
||||||
|
import "@/styles/globals.css";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Providers } from "./providers";
|
import { Providers } from "./providers";
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ export default async function ProjectsPage() {
|
|||||||
// Group links by project ID
|
// Group links by project ID
|
||||||
const linksByProject = new Map<number, PayloadLink[]>();
|
const linksByProject = new Map<number, PayloadLink[]>();
|
||||||
for (const link of allLinks) {
|
for (const link of allLinks) {
|
||||||
const projectId = typeof link.project === "number" ? link.project : link.project.id;
|
const projectId =
|
||||||
|
typeof link.project === "number" ? link.project : link.project.id;
|
||||||
if (!linksByProject.has(projectId)) {
|
if (!linksByProject.has(projectId)) {
|
||||||
linksByProject.set(projectId, []);
|
linksByProject.set(projectId, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { handleServerFunctions, RootLayout } from "@payloadcms/next/layouts";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { importMap } from "./admin/importMap.js";
|
import { importMap } from "./admin/importMap.js";
|
||||||
import "./custom.scss";
|
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|||||||
@@ -224,9 +224,7 @@ export async function GET(req: Request) {
|
|||||||
const urls = allLinks
|
const urls = allLinks
|
||||||
.filter((link) => {
|
.filter((link) => {
|
||||||
const projectId =
|
const projectId =
|
||||||
typeof link.project === "number"
|
typeof link.project === "number" ? link.project : link.project.id;
|
||||||
? link.project
|
|
||||||
: link.project.id;
|
|
||||||
return projectId === project.id;
|
return projectId === project.id;
|
||||||
})
|
})
|
||||||
.map((link) => link.url)
|
.map((link) => link.url)
|
||||||
|
|||||||
@@ -41,7 +41,11 @@ const Dots = ({ className }: DotsProps) => {
|
|||||||
|
|
||||||
function addPoints() {
|
function addPoints() {
|
||||||
for (let x = -SPACING / 2; x < w.current + SPACING; x += SPACING) {
|
for (let x = -SPACING / 2; x < w.current + SPACING; x += SPACING) {
|
||||||
for (let y = -SPACING / 2; y < h.current + offsetY + SPACING; y += SPACING) {
|
for (
|
||||||
|
let y = -SPACING / 2;
|
||||||
|
y < h.current + offsetY + SPACING;
|
||||||
|
y += SPACING
|
||||||
|
) {
|
||||||
const id = `${x}-${y}`;
|
const id = `${x}-${y}`;
|
||||||
if (pointIds.has(id)) continue;
|
if (pointIds.has(id)) continue;
|
||||||
pointIds.add(id);
|
pointIds.add(id);
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
import React, { useRef } from "react";
|
|
||||||
import { useOnClickOutside, useToggle } from "usehooks-ts";
|
|
||||||
import { cn, isHoverable } from "@/utils/helpers";
|
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import Balancer from "react-wrap-balancer";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { type LinkIcon, LinkIcons } from "@/utils/types";
|
|
||||||
import DependentImage from "@/components/DependentImage";
|
|
||||||
|
|
||||||
type ItemCardProps = {
|
|
||||||
banner: string;
|
|
||||||
bannerSettings?: { quality: number };
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
links?: LinkIcon[];
|
|
||||||
location: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ItemCard = ({
|
|
||||||
banner,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
links,
|
|
||||||
location,
|
|
||||||
bannerSettings,
|
|
||||||
}: ItemCardProps) => {
|
|
||||||
const itemRef = useRef<HTMLDivElement>(null);
|
|
||||||
const mobileButtonRef = useRef<HTMLAnchorElement>(null);
|
|
||||||
const [active, toggleActive, setActive] = useToggle();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// @ts-expect-error Some kind of regression in usehooks-ts causes the useOnClickOutside hook to not accept 'null' types
|
|
||||||
useOnClickOutside(itemRef, (event) => {
|
|
||||||
if (
|
|
||||||
mobileButtonRef.current != null &&
|
|
||||||
mobileButtonRef.current?.contains(event.target as Node)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
else setActive(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
const navigate = () => {
|
|
||||||
if (!isHoverable()) toggleActive();
|
|
||||||
else {
|
|
||||||
router.push(location);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
ref={itemRef}
|
|
||||||
className={cn(
|
|
||||||
"item [&:not(:first-child)]:mt-3",
|
|
||||||
active ? "active" : null,
|
|
||||||
)}
|
|
||||||
onClick={navigate}
|
|
||||||
>
|
|
||||||
<DependentImage
|
|
||||||
fill
|
|
||||||
src={banner}
|
|
||||||
quality={bannerSettings?.quality ?? 75}
|
|
||||||
className={(loaded) => cn("object-cover", loaded ? null : "blur-xl")}
|
|
||||||
alt={`Banner for ${title}`}
|
|
||||||
/>
|
|
||||||
<div className="elements m-2 grid h-full grid-cols-12 px-1 sm:px-4">
|
|
||||||
<div className="col-span-12 max-h-full overflow-hidden pb-2 pl-2 drop-shadow-2xl sm:col-span-9 md:p-1 lg:col-span-8">
|
|
||||||
<Link
|
|
||||||
href={{ pathname: location }}
|
|
||||||
className="font-roboto text-lg font-semibold sm:text-2xl md:text-3xl"
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</Link>
|
|
||||||
<div
|
|
||||||
className="description mt-0 overflow-hidden text-base font-light sm:text-xl md:mt-1.5 md:text-xl"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
navigate();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Balancer>
|
|
||||||
<ReactMarkdown>{description}</ReactMarkdown>
|
|
||||||
</Balancer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{(links?.length ?? 0) > 0 ? (
|
|
||||||
<div className="col-span-3 hidden max-h-full w-full justify-end sm:flex md:py-5 lg:col-span-4">
|
|
||||||
<div className="icon-grid grid aspect-square grid-cols-2 grid-rows-2 p-2 md:gap-3">
|
|
||||||
{links!.map(({ icon, location, newTab }) => (
|
|
||||||
<Link
|
|
||||||
key={location}
|
|
||||||
href={location}
|
|
||||||
target={(newTab ?? true) ? "_blank" : "_self"}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{LinkIcons[icon]?.({})}
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Link
|
|
||||||
aria-disabled={!active}
|
|
||||||
ref={mobileButtonRef}
|
|
||||||
href={active ? { pathname: location } : {}}
|
|
||||||
className={cn(
|
|
||||||
"flex w-full items-center justify-center rounded border border-zinc-900 bg-zinc-800 shadow transition-all",
|
|
||||||
active ? "h-9 p-2 opacity-100" : "h-0 p-0 opacity-0",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
Learn More
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ItemCard;
|
|
||||||
@@ -0,0 +1,617 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file was automatically generated by Payload.
|
||||||
|
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||||
|
* and re-run `payload generate:db-schema` to regenerate this file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {} from "@payloadcms/db-postgres";
|
||||||
|
import {
|
||||||
|
pgTable,
|
||||||
|
index,
|
||||||
|
uniqueIndex,
|
||||||
|
foreignKey,
|
||||||
|
integer,
|
||||||
|
varchar,
|
||||||
|
timestamp,
|
||||||
|
serial,
|
||||||
|
numeric,
|
||||||
|
boolean,
|
||||||
|
jsonb,
|
||||||
|
pgEnum,
|
||||||
|
} from "@payloadcms/db-postgres/drizzle/pg-core";
|
||||||
|
import { sql, relations } from "@payloadcms/db-postgres/drizzle";
|
||||||
|
export const enum_projects_status = pgEnum("enum_projects_status", [
|
||||||
|
"draft",
|
||||||
|
"published",
|
||||||
|
"archived",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const users_sessions = pgTable(
|
||||||
|
"users_sessions",
|
||||||
|
{
|
||||||
|
_order: integer("_order").notNull(),
|
||||||
|
_parentID: integer("_parent_id").notNull(),
|
||||||
|
id: varchar("id").primaryKey(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
expiresAt: timestamp("expires_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}).notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("users_sessions_order_idx").on(columns._order),
|
||||||
|
index("users_sessions_parent_id_idx").on(columns._parentID),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["_parentID"]],
|
||||||
|
foreignColumns: [users.id],
|
||||||
|
name: "users_sessions_parent_id_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const users = pgTable(
|
||||||
|
"users",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
email: varchar("email").notNull(),
|
||||||
|
resetPasswordToken: varchar("reset_password_token"),
|
||||||
|
resetPasswordExpiration: timestamp("reset_password_expiration", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
salt: varchar("salt"),
|
||||||
|
hash: varchar("hash"),
|
||||||
|
loginAttempts: numeric("login_attempts", { mode: "number" }).default(0),
|
||||||
|
lockUntil: timestamp("lock_until", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("users_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("users_created_at_idx").on(columns.createdAt),
|
||||||
|
uniqueIndex("users_email_idx").on(columns.email),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const media = pgTable(
|
||||||
|
"media",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
alt: varchar("alt").notNull(),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
url: varchar("url"),
|
||||||
|
thumbnailURL: varchar("thumbnail_u_r_l"),
|
||||||
|
filename: varchar("filename"),
|
||||||
|
mimeType: varchar("mime_type"),
|
||||||
|
filesize: numeric("filesize", { mode: "number" }),
|
||||||
|
width: numeric("width", { mode: "number" }),
|
||||||
|
height: numeric("height", { mode: "number" }),
|
||||||
|
focalX: numeric("focal_x", { mode: "number" }),
|
||||||
|
focalY: numeric("focal_y", { mode: "number" }),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("media_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("media_created_at_idx").on(columns.createdAt),
|
||||||
|
uniqueIndex("media_filename_idx").on(columns.filename),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const projects = pgTable(
|
||||||
|
"projects",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
description: varchar("description").notNull(),
|
||||||
|
shortDescription: varchar("short_description").notNull(),
|
||||||
|
icon: varchar("icon"),
|
||||||
|
status: enum_projects_status("status").notNull().default("draft"),
|
||||||
|
featured: boolean("featured").default(false),
|
||||||
|
autocheckUpdated: boolean("autocheck_updated").default(false),
|
||||||
|
lastUpdated: timestamp("last_updated", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
wakatimeOffset: numeric("wakatime_offset", { mode: "number" }),
|
||||||
|
bannerImage: integer("banner_image_id").references(() => media.id, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("projects_banner_image_idx").on(columns.bannerImage),
|
||||||
|
index("projects_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("projects_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const projects_rels = pgTable(
|
||||||
|
"projects_rels",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
order: integer("order"),
|
||||||
|
parent: integer("parent_id").notNull(),
|
||||||
|
path: varchar("path").notNull(),
|
||||||
|
technologiesID: integer("technologies_id"),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("projects_rels_order_idx").on(columns.order),
|
||||||
|
index("projects_rels_parent_idx").on(columns.parent),
|
||||||
|
index("projects_rels_path_idx").on(columns.path),
|
||||||
|
index("projects_rels_technologies_id_idx").on(columns.technologiesID),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["parent"]],
|
||||||
|
foreignColumns: [projects.id],
|
||||||
|
name: "projects_rels_parent_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["technologiesID"]],
|
||||||
|
foreignColumns: [technologies.id],
|
||||||
|
name: "projects_rels_technologies_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const technologies = pgTable(
|
||||||
|
"technologies",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name").notNull(),
|
||||||
|
url: varchar("url"),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("technologies_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("technologies_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const links = pgTable(
|
||||||
|
"links",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
url: varchar("url").notNull(),
|
||||||
|
icon: varchar("icon"),
|
||||||
|
description: varchar("description"),
|
||||||
|
project: integer("project_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => projects.id, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("links_project_idx").on(columns.project),
|
||||||
|
index("links_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("links_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const payload_locked_documents = pgTable(
|
||||||
|
"payload_locked_documents",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
globalSlug: varchar("global_slug"),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("payload_locked_documents_global_slug_idx").on(columns.globalSlug),
|
||||||
|
index("payload_locked_documents_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("payload_locked_documents_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const payload_locked_documents_rels = pgTable(
|
||||||
|
"payload_locked_documents_rels",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
order: integer("order"),
|
||||||
|
parent: integer("parent_id").notNull(),
|
||||||
|
path: varchar("path").notNull(),
|
||||||
|
usersID: integer("users_id"),
|
||||||
|
mediaID: integer("media_id"),
|
||||||
|
projectsID: integer("projects_id"),
|
||||||
|
technologiesID: integer("technologies_id"),
|
||||||
|
linksID: integer("links_id"),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("payload_locked_documents_rels_order_idx").on(columns.order),
|
||||||
|
index("payload_locked_documents_rels_parent_idx").on(columns.parent),
|
||||||
|
index("payload_locked_documents_rels_path_idx").on(columns.path),
|
||||||
|
index("payload_locked_documents_rels_users_id_idx").on(columns.usersID),
|
||||||
|
index("payload_locked_documents_rels_media_id_idx").on(columns.mediaID),
|
||||||
|
index("payload_locked_documents_rels_projects_id_idx").on(
|
||||||
|
columns.projectsID,
|
||||||
|
),
|
||||||
|
index("payload_locked_documents_rels_technologies_id_idx").on(
|
||||||
|
columns.technologiesID,
|
||||||
|
),
|
||||||
|
index("payload_locked_documents_rels_links_id_idx").on(columns.linksID),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["parent"]],
|
||||||
|
foreignColumns: [payload_locked_documents.id],
|
||||||
|
name: "payload_locked_documents_rels_parent_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["usersID"]],
|
||||||
|
foreignColumns: [users.id],
|
||||||
|
name: "payload_locked_documents_rels_users_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["mediaID"]],
|
||||||
|
foreignColumns: [media.id],
|
||||||
|
name: "payload_locked_documents_rels_media_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["projectsID"]],
|
||||||
|
foreignColumns: [projects.id],
|
||||||
|
name: "payload_locked_documents_rels_projects_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["technologiesID"]],
|
||||||
|
foreignColumns: [technologies.id],
|
||||||
|
name: "payload_locked_documents_rels_technologies_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["linksID"]],
|
||||||
|
foreignColumns: [links.id],
|
||||||
|
name: "payload_locked_documents_rels_links_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const payload_preferences = pgTable(
|
||||||
|
"payload_preferences",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
key: varchar("key"),
|
||||||
|
value: jsonb("value"),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("payload_preferences_key_idx").on(columns.key),
|
||||||
|
index("payload_preferences_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("payload_preferences_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const payload_preferences_rels = pgTable(
|
||||||
|
"payload_preferences_rels",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
order: integer("order"),
|
||||||
|
parent: integer("parent_id").notNull(),
|
||||||
|
path: varchar("path").notNull(),
|
||||||
|
usersID: integer("users_id"),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("payload_preferences_rels_order_idx").on(columns.order),
|
||||||
|
index("payload_preferences_rels_parent_idx").on(columns.parent),
|
||||||
|
index("payload_preferences_rels_path_idx").on(columns.path),
|
||||||
|
index("payload_preferences_rels_users_id_idx").on(columns.usersID),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["parent"]],
|
||||||
|
foreignColumns: [payload_preferences.id],
|
||||||
|
name: "payload_preferences_rels_parent_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
foreignKey({
|
||||||
|
columns: [columns["usersID"]],
|
||||||
|
foreignColumns: [users.id],
|
||||||
|
name: "payload_preferences_rels_users_fk",
|
||||||
|
}).onDelete("cascade"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const payload_migrations = pgTable(
|
||||||
|
"payload_migrations",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
name: varchar("name"),
|
||||||
|
batch: numeric("batch", { mode: "number" }),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
})
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
},
|
||||||
|
(columns) => [
|
||||||
|
index("payload_migrations_updated_at_idx").on(columns.updatedAt),
|
||||||
|
index("payload_migrations_created_at_idx").on(columns.createdAt),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const metadata = pgTable(
|
||||||
|
"metadata",
|
||||||
|
{
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
tagline: varchar("tagline").notNull(),
|
||||||
|
resume: integer("resume_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => media.id, {
|
||||||
|
onDelete: "set null",
|
||||||
|
}),
|
||||||
|
resumeFilename: varchar("resume_filename"),
|
||||||
|
updatedAt: timestamp("updated_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
createdAt: timestamp("created_at", {
|
||||||
|
mode: "string",
|
||||||
|
withTimezone: true,
|
||||||
|
precision: 3,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
(columns) => [index("metadata_resume_idx").on(columns.resume)],
|
||||||
|
);
|
||||||
|
|
||||||
|
export const relations_users_sessions = relations(
|
||||||
|
users_sessions,
|
||||||
|
({ one }) => ({
|
||||||
|
_parentID: one(users, {
|
||||||
|
fields: [users_sessions._parentID],
|
||||||
|
references: [users.id],
|
||||||
|
relationName: "sessions",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const relations_users = relations(users, ({ many }) => ({
|
||||||
|
sessions: many(users_sessions, {
|
||||||
|
relationName: "sessions",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
export const relations_media = relations(media, () => ({}));
|
||||||
|
export const relations_projects_rels = relations(projects_rels, ({ one }) => ({
|
||||||
|
parent: one(projects, {
|
||||||
|
fields: [projects_rels.parent],
|
||||||
|
references: [projects.id],
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
technologiesID: one(technologies, {
|
||||||
|
fields: [projects_rels.technologiesID],
|
||||||
|
references: [technologies.id],
|
||||||
|
relationName: "technologies",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
export const relations_projects = relations(projects, ({ one, many }) => ({
|
||||||
|
bannerImage: one(media, {
|
||||||
|
fields: [projects.bannerImage],
|
||||||
|
references: [media.id],
|
||||||
|
relationName: "bannerImage",
|
||||||
|
}),
|
||||||
|
_rels: many(projects_rels, {
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
export const relations_technologies = relations(technologies, () => ({}));
|
||||||
|
export const relations_links = relations(links, ({ one }) => ({
|
||||||
|
project: one(projects, {
|
||||||
|
fields: [links.project],
|
||||||
|
references: [projects.id],
|
||||||
|
relationName: "project",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
export const relations_payload_locked_documents_rels = relations(
|
||||||
|
payload_locked_documents_rels,
|
||||||
|
({ one }) => ({
|
||||||
|
parent: one(payload_locked_documents, {
|
||||||
|
fields: [payload_locked_documents_rels.parent],
|
||||||
|
references: [payload_locked_documents.id],
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
usersID: one(users, {
|
||||||
|
fields: [payload_locked_documents_rels.usersID],
|
||||||
|
references: [users.id],
|
||||||
|
relationName: "users",
|
||||||
|
}),
|
||||||
|
mediaID: one(media, {
|
||||||
|
fields: [payload_locked_documents_rels.mediaID],
|
||||||
|
references: [media.id],
|
||||||
|
relationName: "media",
|
||||||
|
}),
|
||||||
|
projectsID: one(projects, {
|
||||||
|
fields: [payload_locked_documents_rels.projectsID],
|
||||||
|
references: [projects.id],
|
||||||
|
relationName: "projects",
|
||||||
|
}),
|
||||||
|
technologiesID: one(technologies, {
|
||||||
|
fields: [payload_locked_documents_rels.technologiesID],
|
||||||
|
references: [technologies.id],
|
||||||
|
relationName: "technologies",
|
||||||
|
}),
|
||||||
|
linksID: one(links, {
|
||||||
|
fields: [payload_locked_documents_rels.linksID],
|
||||||
|
references: [links.id],
|
||||||
|
relationName: "links",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const relations_payload_locked_documents = relations(
|
||||||
|
payload_locked_documents,
|
||||||
|
({ many }) => ({
|
||||||
|
_rels: many(payload_locked_documents_rels, {
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const relations_payload_preferences_rels = relations(
|
||||||
|
payload_preferences_rels,
|
||||||
|
({ one }) => ({
|
||||||
|
parent: one(payload_preferences, {
|
||||||
|
fields: [payload_preferences_rels.parent],
|
||||||
|
references: [payload_preferences.id],
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
usersID: one(users, {
|
||||||
|
fields: [payload_preferences_rels.usersID],
|
||||||
|
references: [users.id],
|
||||||
|
relationName: "users",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const relations_payload_preferences = relations(
|
||||||
|
payload_preferences,
|
||||||
|
({ many }) => ({
|
||||||
|
_rels: many(payload_preferences_rels, {
|
||||||
|
relationName: "_rels",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
export const relations_payload_migrations = relations(
|
||||||
|
payload_migrations,
|
||||||
|
() => ({}),
|
||||||
|
);
|
||||||
|
export const relations_metadata = relations(metadata, ({ one }) => ({
|
||||||
|
resume: one(media, {
|
||||||
|
fields: [metadata.resume],
|
||||||
|
references: [media.id],
|
||||||
|
relationName: "resume",
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
type DatabaseSchema = {
|
||||||
|
enum_projects_status: typeof enum_projects_status;
|
||||||
|
users_sessions: typeof users_sessions;
|
||||||
|
users: typeof users;
|
||||||
|
media: typeof media;
|
||||||
|
projects: typeof projects;
|
||||||
|
projects_rels: typeof projects_rels;
|
||||||
|
technologies: typeof technologies;
|
||||||
|
links: typeof links;
|
||||||
|
payload_locked_documents: typeof payload_locked_documents;
|
||||||
|
payload_locked_documents_rels: typeof payload_locked_documents_rels;
|
||||||
|
payload_preferences: typeof payload_preferences;
|
||||||
|
payload_preferences_rels: typeof payload_preferences_rels;
|
||||||
|
payload_migrations: typeof payload_migrations;
|
||||||
|
metadata: typeof metadata;
|
||||||
|
relations_users_sessions: typeof relations_users_sessions;
|
||||||
|
relations_users: typeof relations_users;
|
||||||
|
relations_media: typeof relations_media;
|
||||||
|
relations_projects_rels: typeof relations_projects_rels;
|
||||||
|
relations_projects: typeof relations_projects;
|
||||||
|
relations_technologies: typeof relations_technologies;
|
||||||
|
relations_links: typeof relations_links;
|
||||||
|
relations_payload_locked_documents_rels: typeof relations_payload_locked_documents_rels;
|
||||||
|
relations_payload_locked_documents: typeof relations_payload_locked_documents;
|
||||||
|
relations_payload_preferences_rels: typeof relations_payload_preferences_rels;
|
||||||
|
relations_payload_preferences: typeof relations_payload_preferences;
|
||||||
|
relations_payload_migrations: typeof relations_payload_migrations;
|
||||||
|
relations_metadata: typeof relations_metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare module "@payloadcms/db-postgres" {
|
||||||
|
export interface GeneratedDatabaseSchema {
|
||||||
|
schema: DatabaseSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
/**
|
/**
|
||||||
* This file was automatically generated by Payload.
|
* This file was automatically generated by Payload.
|
||||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
/* Custom colors */
|
||||||
|
--color-zinc-850: #1d1d20;
|
||||||
|
|
||||||
|
/* Custom font sizes */
|
||||||
|
--font-size-10xl: 10rem;
|
||||||
|
|
||||||
|
/* Drop shadows */
|
||||||
|
--drop-shadow-extreme: 0 0 50px black;
|
||||||
|
|
||||||
|
/* Font families */
|
||||||
|
--font-inter: "Inter", sans-serif;
|
||||||
|
--font-roboto: "Roboto", sans-serif;
|
||||||
|
--font-mono: "Roboto Mono", monospace;
|
||||||
|
--font-hanken: "Hanken Grotesk", sans-serif;
|
||||||
|
|
||||||
|
/* Background images */
|
||||||
|
--background-image-gradient-radial: radial-gradient(
|
||||||
|
50% 50% at 50% 50%,
|
||||||
|
var(--tw-gradient-stops)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
--animate-bg-fast: fade 0.5s ease-in-out 0.5s forwards;
|
||||||
|
--animate-bg: fade 1.2s ease-in-out 1.1s forwards;
|
||||||
|
--animate-fade-in: fade-in 2.5s ease-in-out forwards;
|
||||||
|
--animate-title: title 3s ease-out forwards;
|
||||||
|
--animate-fade-left: fade-left 3s ease-in-out forwards;
|
||||||
|
--animate-fade-right: fade-right 3s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade {
|
||||||
|
0% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-left {
|
||||||
|
0% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-right {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes title {
|
||||||
|
0% {
|
||||||
|
line-height: 0%;
|
||||||
|
letter-spacing: 0.25em;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
line-height: 0%;
|
||||||
|
opacity: 0%;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
line-height: 100%;
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
@apply font-inter overflow-x-hidden text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.description {
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
@import url("https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@900&display=swap");
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap");
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap");
|
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@200;300;400;500;700;900&display=swap");
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
// Used for ItemCard
|
|
||||||
@mixin active {
|
|
||||||
.elements {
|
|
||||||
@apply grid opacity-100;
|
|
||||||
}
|
|
||||||
|
|
||||||
> img {
|
|
||||||
@apply blur-2xl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
@apply overflow-x-hidden font-inter text-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
@apply pointer-events-auto relative aspect-[7/2] w-full cursor-pointer overflow-hidden rounded transition-all sm:h-[14rem] md:h-[16rem] lg:aspect-[5/3];
|
|
||||||
> img {
|
|
||||||
@apply rounded transition-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elements {
|
|
||||||
@apply hidden opacity-0 transition-all delay-100;
|
|
||||||
> * {
|
|
||||||
// z-index: 30;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
&:hover {
|
|
||||||
@include active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
@include active;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-grid {
|
|
||||||
direction: rtl;
|
|
||||||
@apply max-h-full min-h-0 min-w-0 max-w-full;
|
|
||||||
|
|
||||||
> a > svg {
|
|
||||||
@apply h-full w-full;
|
|
||||||
}
|
|
||||||
|
|
||||||
> svg,
|
|
||||||
a {
|
|
||||||
width: 75%;
|
|
||||||
height: 75%;
|
|
||||||
@apply m-auto aspect-square text-white opacity-80 drop-shadow-md transition-transform hover:scale-[120%] hover:opacity-100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
hyphens: auto;
|
|
||||||
@screen md {
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
@apply h-full;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
import create from "@kodingdotninja/use-tailwind-breakpoint";
|
|
||||||
import resolveConfig from "tailwindcss/resolveConfig";
|
|
||||||
import tailwindConfig from "@/../tailwind.config.cjs";
|
|
||||||
|
|
||||||
import { clsx, type ClassValue } from "clsx";
|
import { clsx, type ClassValue } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
@@ -24,7 +20,3 @@ const hoverableQuery: MediaQueryList | null = isClient()
|
|||||||
export function isHoverable() {
|
export function isHoverable() {
|
||||||
return hoverableQuery?.matches;
|
return hoverableQuery?.matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = resolveConfig(tailwindConfig);
|
|
||||||
export const { useBreakpoint, useBreakpointValue, useBreakpointEffect } =
|
|
||||||
create(config.theme!.screens);
|
|
||||||
|
|||||||
+6
-7
@@ -1,6 +1,5 @@
|
|||||||
import type { IconType } from "react-icons";
|
import { Github, ExternalLink, Link } from "lucide-react";
|
||||||
import { AiFillGithub, AiOutlineLink } from "react-icons/ai";
|
import type { LucideIcon } from "lucide-react";
|
||||||
import { RxOpenInNewWindow } from "react-icons/rx";
|
|
||||||
|
|
||||||
// Promise.allSettled type guards
|
// Promise.allSettled type guards
|
||||||
export const isFulfilled = <T>(
|
export const isFulfilled = <T>(
|
||||||
@@ -10,10 +9,10 @@ export const isRejected = <T>(
|
|||||||
p: PromiseSettledResult<T>,
|
p: PromiseSettledResult<T>,
|
||||||
): p is PromiseRejectedResult => p.status === "rejected";
|
): p is PromiseRejectedResult => p.status === "rejected";
|
||||||
|
|
||||||
export const LinkIcons: Record<string, IconType> = {
|
export const LinkIcons: Record<string, LucideIcon> = {
|
||||||
github: AiFillGithub,
|
github: Github,
|
||||||
external: RxOpenInNewWindow,
|
external: ExternalLink,
|
||||||
link: AiOutlineLink,
|
link: Link,
|
||||||
};
|
};
|
||||||
export type LinkIcon = {
|
export type LinkIcon = {
|
||||||
icon: keyof typeof LinkIcons;
|
icon: keyof typeof LinkIcons;
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
module.exports = {
|
|
||||||
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
zinc: {
|
|
||||||
850: "#1D1D20",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fontSize: {
|
|
||||||
"10xl": "10rem",
|
|
||||||
},
|
|
||||||
typography: {
|
|
||||||
DEFAULT: {
|
|
||||||
css: {
|
|
||||||
"code::before": {
|
|
||||||
content: '""',
|
|
||||||
},
|
|
||||||
"code::after": {
|
|
||||||
content: '""',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
quoteless: {
|
|
||||||
css: {
|
|
||||||
"blockquote p:first-of-type::before": { content: "none" },
|
|
||||||
"blockquote p:first-of-type::after": { content: "none" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dropShadow: {
|
|
||||||
extreme: "0 0 50px black",
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
inter: ['"Inter"', "sans-serif"],
|
|
||||||
roboto: ['"Roboto"', "sans-serif"],
|
|
||||||
mono: ['"Roboto Mono"', "monospace"],
|
|
||||||
hanken: ['"Hanken Grotesk"', "sans-serif"],
|
|
||||||
},
|
|
||||||
backgroundImage: {
|
|
||||||
"gradient-radial":
|
|
||||||
"radial-gradient(50% 50% at 50% 50%, var(--tw-gradient-stops))",
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
"bg-fast": "fade 0.5s ease-in-out 0.5s forwards",
|
|
||||||
bg: "fade 1.2s ease-in-out 1.1s forwards",
|
|
||||||
"fade-in": "fade-in 2.5s ease-in-out forwards",
|
|
||||||
title: "title 3s ease-out forwards",
|
|
||||||
"fade-left": "fade-left 3s ease-in-out forwards",
|
|
||||||
"fade-right": "fade-right 3s ease-in-out forwards",
|
|
||||||
},
|
|
||||||
keyframes: {
|
|
||||||
fade: {
|
|
||||||
"0%": {
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
"100%": {
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"fade-in": {
|
|
||||||
"0%": {
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
"75%": {
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
"100%": {
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"fade-left": {
|
|
||||||
"0%": {
|
|
||||||
transform: "translateX(100%)",
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
|
|
||||||
"30%": {
|
|
||||||
transform: "translateX(0%)",
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
"100%": {
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"fade-right": {
|
|
||||||
"0%": {
|
|
||||||
transform: "translateX(-100%)",
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
|
|
||||||
"30%": {
|
|
||||||
transform: "translateX(0%)",
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
"100%": {
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
"0%": {
|
|
||||||
"line-height": "0%",
|
|
||||||
"letter-spacing": "0.25em",
|
|
||||||
opacity: "0",
|
|
||||||
},
|
|
||||||
"25%": {
|
|
||||||
"line-height": "0%",
|
|
||||||
opacity: "0%",
|
|
||||||
},
|
|
||||||
"80%": {
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
|
|
||||||
"100%": {
|
|
||||||
"line-height": "100%",
|
|
||||||
opacity: "100%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [require("@tailwindcss/typography")],
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user