(2)].map((_, index) => (
@@ -291,7 +309,7 @@ export const ReportPictureSkeleton: React.FC = ({ pulse = true }) => {
return (
= ({ pulse = true }) => {
)}
/>
diff --git a/apps/nextjs/src/app/workspaces/error.tsx b/apps/nextjs/src/app/(dashboard)/error.tsx
similarity index 69%
rename from apps/nextjs/src/app/workspaces/error.tsx
rename to apps/nextjs/src/app/(dashboard)/error.tsx
index 6f4bc0f2..2046c8c0 100644
--- a/apps/nextjs/src/app/workspaces/error.tsx
+++ b/apps/nextjs/src/app/(dashboard)/error.tsx
@@ -3,8 +3,8 @@
import { useEffect } from "react";
import Image from "next/image";
-import Button from "../_components/button";
-import Heading from "../_components/heading";
+import { Button } from "@acme/ui/button";
+import { Title } from "@acme/ui/title";
export default function Error({
error,
@@ -18,18 +18,18 @@ export default function Error({
}, [error]);
return (
-
+
-
- Oops, looks like something went wrong!
-
+
+ Oops, looks like something went wrong!
+
We track these errors automatically, but if the problem persists feel
free to{" "}
diff --git a/apps/nextjs/src/app/(dashboard)/layout.tsx b/apps/nextjs/src/app/(dashboard)/layout.tsx
new file mode 100644
index 00000000..41e9e600
--- /dev/null
+++ b/apps/nextjs/src/app/(dashboard)/layout.tsx
@@ -0,0 +1,19 @@
+import { redirect } from "next/navigation";
+
+import { auth } from "@acme/auth";
+
+import routes from "~/_utils/routes";
+
+export default async function WorkspaceLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const session = await auth();
+
+ if (!session) {
+ redirect(routes.home);
+ }
+
+ return <>{children}>;
+}
diff --git a/apps/nextjs/src/app/(dashboard)/workspaces/[id]/layout.tsx b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/layout.tsx
new file mode 100644
index 00000000..25b8e54c
--- /dev/null
+++ b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/layout.tsx
@@ -0,0 +1,60 @@
+import Link from "next/link";
+import { notFound } from "next/navigation";
+
+import { Button } from "@acme/ui/button";
+import { Icons } from "@acme/ui/icons";
+import { TooltipProvider } from "@acme/ui/tooltip";
+
+import { Sidebar } from "~/_components/workspaces/navigation";
+import routes from "~/_utils/routes";
+import { api } from "~/trpc/server";
+
+export default async function WorkspaceLayout({
+ children,
+ params,
+}: {
+ children: React.ReactNode;
+ params: { id: string };
+}) {
+ const workspace = api.workspace.byId({ id: params.id });
+
+ if (!(await workspace)) {
+ notFound();
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ Workspaces
+
+
+ Teams Overview
+
+
+ Share
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/apps/nextjs/src/app/(dashboard)/workspaces/[id]/manage/[tab]/page.tsx b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/manage/[tab]/page.tsx
new file mode 100644
index 00000000..95a74ad0
--- /dev/null
+++ b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/manage/[tab]/page.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useParams, useRouter } from "next/navigation";
+
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@acme/ui/tabs";
+
+import { MembersTab } from "~/_components/settings/members-tab";
+import { ProjectsTab } from "~/_components/settings/projects-tab";
+import { SettingsTab } from "~/_components/settings/settings-tab";
+import routes from "~/_utils/routes";
+
+export default function Projects() {
+ const params = useParams<{ id: string; tab: string }>();
+ const router = useRouter();
+ const [activeTab, setActiveTab] = useState(params.tab || "members");
+
+ const handleTabChange = (value: string) => {
+ setActiveTab(value);
+
+ router.replace(`${routes.workspaceManage(params.id)}/${params.tab}`);
+ };
+
+ useEffect(() => {
+ setActiveTab(params.tab);
+ }, [params.tab]);
+
+ return (
+
+
+
+ Members
+ Projects
+ Settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/nextjs/src/app/(dashboard)/workspaces/[id]/page.tsx b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/page.tsx
new file mode 100644
index 00000000..a1bd1dbe
--- /dev/null
+++ b/apps/nextjs/src/app/(dashboard)/workspaces/[id]/page.tsx
@@ -0,0 +1,86 @@
+import { Suspense } from "react";
+import { cookies } from "next/headers";
+
+import { auth } from "@acme/auth";
+import { cn } from "@acme/ui";
+
+import { ManageWeek } from "~/_components/workspace/manage-week";
+import {
+ ReportCardSkeleton,
+ ReportPictureSkeleton,
+} from "~/_components/workspace/reports";
+import { Sprint } from "~/_components/workspace/Sprint";
+import { WeekList } from "~/_components/workspace/WeekList";
+import { getDaysOfWeek, getWeekdays } from "~/_utils/days";
+import { api } from "~/trpc/server";
+
+export const runtime = "edge";
+
+export default async function WorkspacePage({
+ params,
+ searchParams,
+}: {
+ params: { id: string };
+ searchParams?: Record;
+}) {
+ const session = await auth();
+
+ const cookieStore = cookies();
+ const weekend = JSON.parse(cookieStore.get("weekend")?.value ?? null);
+
+ const today = searchParams?.today as string;
+ const weekdays = getDaysOfWeek(today);
+
+ // // You can await this here if you don't want to show Suspense fallback below
+ const sprints = api.sprint.byDateRange({
+ from: weekdays.at(0)!.date,
+ to: weekdays.at(-1)!.date,
+ workspaceId: params.id,
+ });
+
+ const users = api.user.byWorkspaceId({
+ workspaceId: params.id,
+ });
+
+ const projects = api.project.byWorkspaceId({
+ id: params.id,
+ });
+
+ const cards = [...Array(getWeekdays(weekend))];
+
+ return (
+ <>
+
+
+
+ {[...Array(2)].map((_, index) => (
+
+
+ {cards.map((_, index) => (
+
+ ))}
+
+ ))}
+ >
+ }
+ >
+
+
+ >
+ );
+}
diff --git a/apps/nextjs/src/app/(dashboard)/workspaces/page.tsx b/apps/nextjs/src/app/(dashboard)/workspaces/page.tsx
new file mode 100644
index 00000000..28504893
--- /dev/null
+++ b/apps/nextjs/src/app/(dashboard)/workspaces/page.tsx
@@ -0,0 +1,49 @@
+import { Suspense } from "react";
+
+import { Title } from "@acme/ui/title";
+
+import Footer from "~/_components/footer";
+import Header from "~/_components/header";
+import { CreateWorkspacesModal } from "~/_components/modals";
+import { WorkspaceCardSkeleton, WorkspaceList } from "~/_components/workspaces";
+import { api } from "~/trpc/server";
+
+export const runtime = "edge";
+
+export default async function WorkspacesPage() {
+ // You can await this here if you don't want to show Suspense fallback below
+ const workspaces = api.workspace.all();
+
+ return (
+ <>
+
+
+
+ }
+ >
+
+
+