diff --git a/apps/web/src/pages/[...workspaceSlug]/index.tsx b/apps/web/src/pages/[...workspaceSlug]/index.tsx new file mode 100644 index 00000000..89d04a52 --- /dev/null +++ b/apps/web/src/pages/[...workspaceSlug]/index.tsx @@ -0,0 +1,5 @@ +import WorkspaceSlugView from "~/views/workspaceSlug"; + +export default function WorkspaceSlugPage() { + return ; +} diff --git a/apps/web/src/views/workspaceSlug/index.tsx b/apps/web/src/views/workspaceSlug/index.tsx new file mode 100644 index 00000000..b1ef788f --- /dev/null +++ b/apps/web/src/views/workspaceSlug/index.tsx @@ -0,0 +1,97 @@ +import Link from "next/link"; +import { useRouter } from "next/router"; + +import { PageHead } from "~/components/PageHead"; +import PatternedBackground from "~/components/PatternedBackground"; +import { api } from "~/utils/api"; + +export default function WorkspaceSlugPage() { + const router = useRouter(); + + const workspaceSlug = Array.isArray(router.query.workspaceSlug) + ? router.query.workspaceSlug[0] + : router.query.workspaceSlug; + + const { data, isLoading } = api.workspace.bySlug.useQuery( + { + workspaceSlug: workspaceSlug ?? "", + }, + { enabled: !!workspaceSlug }, + ); + + const BoardsList = ({ + isLoading, + boards, + workspaceSlug, + }: { + isLoading: boolean; + boards: { publicId: string; name: string }[]; + workspaceSlug: string; + }) => { + if (isLoading) + return ( +
+
+
+
+
+
+ ); + + if (boards.length === 0) return <>; + + return ( +
+ {boards.map((board) => ( + +
+ +

+ {board.name} +

+
+ + ))} +
+ ); + }; + + return ( + <> + + +
+

+ {data?.name} +

+

+ The open source Trello alternative. +

+
+ {data?.boards && workspaceSlug && ( + + )} +
+ + kan.bn + +
+ + ); +} diff --git a/packages/api/src/routers/workspace.ts b/packages/api/src/routers/workspace.ts index 56a3e976..0ae2cb6f 100644 --- a/packages/api/src/routers/workspace.ts +++ b/packages/api/src/routers/workspace.ts @@ -64,6 +64,29 @@ export const workspaceRouter = createTRPCRouter({ code: "NOT_FOUND", }); + return result; + }), + bySlug: publicProcedure + .meta({ + openapi: { + summary: "Get a workspace by slug", + method: "GET", + path: "/workspaces/slug/{workspaceSlug}", + description: "Retrieves a workspace by its slug", + tags: ["Workspaces"], + protect: true, + }, + }) + .input(z.object({ workspaceSlug: z.string().min(3).max(24) })) + .output( + z.custom>>(), + ) + .query(async ({ ctx, input }) => { + const result = await workspaceRepo.getBySlugWithBoards( + ctx.db, + input.workspaceSlug, + ); + return result; }), create: protectedProcedure diff --git a/packages/db/src/repository/workspace.repo.ts b/packages/db/src/repository/workspace.repo.ts index 92866ce5..5cc8a9c3 100644 --- a/packages/db/src/repository/workspace.repo.ts +++ b/packages/db/src/repository/workspace.repo.ts @@ -101,6 +101,32 @@ export const getByPublicIdWithMembers = async ( return data; }; +export const getBySlugWithBoards = async ( + db: SupabaseClient, + workspaceSlug: string, +) => { + const { data } = await db + .from("workspace") + .select( + ` + publicId, + name, + slug, + boards: board ( + publicId, + name + ) + `, + ) + .eq("slug", workspaceSlug) + .is("deletedAt", null) + .is("boards.deletedAt", null) + .limit(1) + .single(); + + return data; +}; + export const getAllByUserId = async ( db: SupabaseClient, userId: string,