diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx
index e5ed191..e42a7f0 100644
--- a/src/app/projects/page.tsx
+++ b/src/app/projects/page.tsx
@@ -1,12 +1,87 @@
'use client'
-import { usePathname } from "next/navigation"
+import { trpc } from "~/app/_trpc/Client";
+import * as Card from "~/components/ui/card";
+import { Badge } from "~/components/ui/badge";
+import { StackBadge } from "~/components/StackBadge";
+
+export default function ProjectsPage() {
+ const { data: projects, isLoading } = trpc.projectv2.listWithStack.useQuery();
+
+ if (isLoading) {
+ return (
+
+ Loading...
+
+ );
+ }
+
+ if (!projects?.length) {
+ return (
+
+ No projects yet.
+
+ );
+ }
-export default function Page() {
- const pathName = usePathname()
return (
-
- {pathName}
+
+ {projects.map((project) => (
+
+
+
+
{project.title}
+
+ {project.sourceType && (
+
+ {project.sourceType === "open" ? "Open Source" : "Closed Source"}
+
+ )}
+ {project.releaseStatus && (
+
+ {project.releaseStatus === "released" ? "Released" : "Unreleased"}
+
+ )}
+
+
+
+ {(project.sourceLink || project.releaseLink || project.techStack?.stackItems?.length) && (
+
+ {(project.sourceLink || project.releaseLink) && (
+
+ {project.sourceLink && (
+
+ Source
+
+ )}
+ {project.releaseLink && (
+
+ Live
+
+ )}
+
+ )}
+ {project.techStack?.stackItems && project.techStack.stackItems.length > 0 && (
+
+ {project.techStack.stackItems.map((item) => (
+
+ ))}
+
+ )}
+
+ )}
+
+ ))}
- )
+ );
}
diff --git a/src/components/StackBadge.tsx b/src/components/StackBadge.tsx
new file mode 100644
index 0000000..c0e143f
--- /dev/null
+++ b/src/components/StackBadge.tsx
@@ -0,0 +1,149 @@
+import { Badge } from "~/components/ui/badge";
+
+interface SvglIcon {
+ light: string;
+ dark: string;
+}
+
+const STACK_META: Record
= {
+ drizzle: {
+ label: "Drizzle ORM",
+ icon: {
+ light: "https://svgl.app/library/drizzle-orm_light.svg",
+ dark: "https://svgl.app/library/drizzle-orm_dark.svg",
+ },
+ },
+ postgres: {
+ label: "PostgreSQL",
+ icon: {
+ light: "https://svgl.app/library/postgresql.svg",
+ dark: "https://svgl.app/library/postgresql.svg",
+ },
+ },
+ nextjs: {
+ label: "Next.js",
+ icon: {
+ light: "https://svgl.app/library/nextjs_icon_dark.svg",
+ dark: "https://svgl.app/library/nextjs_icon_dark.svg",
+ },
+ },
+ react: {
+ label: "React",
+ icon: {
+ light: "https://svgl.app/library/react_light.svg",
+ dark: "https://svgl.app/library/react_dark.svg",
+ },
+ },
+ servercomponents: { label: "Server Components" },
+ php: {
+ label: "PHP",
+ icon: {
+ light: "https://svgl.app/library/php.svg",
+ dark: "https://svgl.app/library/php_dark.svg",
+ },
+ },
+ laravel: {
+ label: "Laravel",
+ icon: {
+ light: "https://svgl.app/library/laravel.svg",
+ dark: "https://svgl.app/library/laravel.svg",
+ },
+ },
+ reactnative: {
+ label: "React Native",
+ icon: {
+ light: "https://svgl.app/library/react_light.svg",
+ dark: "https://svgl.app/library/react_dark.svg",
+ },
+ },
+ "react-native": {
+ label: "React Native",
+ icon: {
+ light: "https://svgl.app/library/react_light.svg",
+ dark: "https://svgl.app/library/react_dark.svg",
+ },
+ },
+ expo: {
+ label: "Expo",
+ icon: {
+ light: "https://svgl.app/library/expo.svg",
+ dark: "https://svgl.app/library/expo.svg",
+ },
+ },
+ mysql: {
+ label: "MySQL",
+ icon: {
+ light: "https://svgl.app/library/mysql-icon-light.svg",
+ dark: "https://svgl.app/library/mysql-icon-dark.svg",
+ },
+ },
+ nginx: {
+ label: "Nginx",
+ icon: {
+ light: "https://svgl.app/library/nginx.svg",
+ dark: "https://svgl.app/library/nginx.svg",
+ },
+ },
+ protobuf: { label: "Protobuf" },
+ grpc: { label: "gRPC" },
+ java: {
+ label: "Java",
+ icon: {
+ light: "https://svgl.app/library/java.svg",
+ dark: "https://svgl.app/library/java.svg",
+ },
+ },
+ graalvm: { label: "GraalVM" },
+ spring: {
+ label: "Spring",
+ icon: {
+ light: "https://svgl.app/library/spring.svg",
+ dark: "https://svgl.app/library/spring.svg",
+ },
+ },
+ aws: {
+ label: "AWS",
+ icon: {
+ light: "https://svgl.app/library/aws_light.svg",
+ dark: "https://svgl.app/library/aws_dark.svg",
+ },
+ },
+ s3: { label: "Amazon S3" },
+ linux: {
+ label: "Linux",
+ icon: {
+ light: "https://svgl.app/library/linux.svg",
+ dark: "https://svgl.app/library/linux.svg",
+ },
+ },
+ debian: { label: "Debian" },
+ htmx: { label: "HTMX" },
+};
+
+export function StackBadge({ item }: { item: string }) {
+ const meta = STACK_META[item] ?? { label: item };
+
+ return (
+
+ {meta.icon && (
+ <>
+
+
+ >
+ )}
+ {meta.label}
+
+ );
+}
diff --git a/src/server/routers/project.ts b/src/server/routers/project.ts
index bd7fcc6..8f3604e 100644
--- a/src/server/routers/project.ts
+++ b/src/server/routers/project.ts
@@ -1,4 +1,12 @@
-import { router } from "~/server/trpc";
+import { publicProcedure, router } from "~/server/trpc";
+import { db } from "~/server/db";
export const projectRouter = router({
+ listWithStack: publicProcedure.query(async () => {
+ return db.query.project.findMany({
+ with: {
+ techStack: true,
+ },
+ });
+ }),
});