Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { MarketingInfo } from "@/components/marketing-info";
import { auth } from "@/lib/auth";
import { redirect } from 'next/navigation'
import { headers } from "next/headers";
import { getServerSession } from "@/lib/server/session";



Expand All @@ -10,9 +9,7 @@ export default async function AuthLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await auth.api.getSession({
headers: await headers(),
});
const session = await getServerSession();

if (session) redirect("/");

Expand Down
5 changes: 2 additions & 3 deletions app/(dashboard)/invite/accept/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { eq, and } from "drizzle-orm";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { databaseDrizzle } from "@/db";
import { invitation, usersProjects } from "@/db/schema";
import { LogoutBtn } from "@/components/LogoutBtn/LogoutBtn";
import { getServerSession } from "@/lib/server/session";

interface PageProps {
searchParams: Promise<{ token?: string }>;
Expand All @@ -19,7 +18,7 @@ export default async function AcceptInvitePage({ searchParams }: PageProps) {
return <InvalidInvite title="Invalid Invitation" action="home" message="No invitation token provided." />;
}

const session = await auth.api.getSession({ headers: await headers() });
const session = await getServerSession();
if (!session?.user.id) {
return (
<Card className="max-w-md mx-auto mt-20">
Expand Down
21 changes: 5 additions & 16 deletions app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,21 @@
import { auth } from "@/lib/auth";
import { redirect } from 'next/navigation'
import { headers } from "next/headers";
import { TooltipProvider } from "@/components/ui/tooltip";
import { ThemeProvider } from "@/components/theme-provider";
import { getServerSession } from "@/lib/server/session";


export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const session = await auth.api.getSession({
headers: await headers(),
});
const session = await getServerSession();

if (!session) redirect("/signin");

return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<TooltipProvider>
{children}
</TooltipProvider>
</ThemeProvider>
<TooltipProvider>
{children}
</TooltipProvider>

);
}
51 changes: 24 additions & 27 deletions app/(dashboard)/projects/[id]/changelog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
import { ChangelogList } from "@/components/ChangelogList/ChangelogList"
import { UpsertChangelog } from "@/components/UpsertChangelog/UpsertChangelog"
import { databaseDrizzle } from "@/db"
import { auth } from "@/lib/auth"
import { permission } from "@/lib/utils"
import { headers } from "next/headers";
import { redirect } from 'next/navigation'
import { getServerSession } from "@/lib/server/session"


export default async function page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const session = await auth.api.getSession({ headers: await headers() });
const [{ id }, session] = await Promise.all([params, getServerSession()])
if (!session?.user.id) return redirect("/signin");

const logs = await databaseDrizzle.query.changelogs.findMany({
where: (c, ops) => ops.eq(c.projectId, id),
with: {
user: {
columns: {
id: true,
name: true,
image: true,
},
with: {
usersProjects: {
where: (p, ops) => ops.eq(p.projectId, id),
columns: {
role: true,
const [logs, memberships] = await Promise.all([
databaseDrizzle.query.changelogs.findMany({
where: (c, ops) => ops.eq(c.projectId, id),
with: {
user: {
columns: {
id: true,
name: true,
image: true,
},
with: {
usersProjects: {
where: (p, ops) => ops.eq(p.projectId, id),
columns: {
role: true,
}
}
}
}
}
}
})


// Get memberships for role checking
const memberships = await databaseDrizzle.query.usersProjects.findMany({
where: (c, ops) => ops.eq(c.projectId, id),
columns: { userId: true, role: true },
});
}),
databaseDrizzle.query.usersProjects.findMany({
where: (c, ops) => ops.eq(c.projectId, id),
columns: { userId: true, role: true },
})
])

const permit = permission(memberships, session.user.id)

Expand Down
98 changes: 47 additions & 51 deletions app/(dashboard)/projects/[id]/feature-requests/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { notFound } from "next/navigation";
import { databaseDrizzle } from "@/db";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { redirect } from 'next/navigation'
import { UpsertFeature } from "@/components/UpsertFeature/UpsertFeature";
import { UpvoteProvider } from "@/contexts/UpvoteProvider";
Expand All @@ -11,6 +9,7 @@ import { feature, featureTags, project, usersProjects } from "@/db/schema";
import { FeatureFilters } from "@/components/FeatureFilters/FeatureFilters";
import { FeatureList } from "@/components/FeatureLists/FeatureList";
import { FeatureDetail } from "@/components/FeatureDetail/FeatureDetail";
import { getServerSession } from "@/lib/server/session";


interface PageProps {
Expand Down Expand Up @@ -57,37 +56,29 @@ export interface QFeature {
}[];
}


type FeatureStatus = QFeature["status"]

export default async function ProjectFeedbackPage({ params, searchParams }: PageProps) {
const { id } = await params;
const {
const [{ id }, {
status = "all",
sort = "most-votes",
q = "",
tags = "",
page = "1",
featureId,
} = await searchParams;
const session = await auth.api.getSession({ headers: await headers() });
}] = await Promise.all([params, searchParams]);
const session = await getServerSession();
if (!session?.user.id) return redirect("/signin");

const currentPage = parseInt(page) || 1;
const pageSize = 10;
const tagIds = tags ? tags.split(",").filter(Boolean) : [];

// Fetch project and its tags
const projectData = await databaseDrizzle.query.project.findFirst({
where: eq(project.id, id),
columns: { name: true },
with: { tags: true },
});
if (!projectData) return notFound();
const featureStatus = status === "all" ? undefined : (status as FeatureStatus)

// Build where clause for features
const featureWhere = and(
eq(feature.projectId, id),
status !== "all" ? eq(feature.status, status as any) : undefined,
featureStatus ? eq(feature.status, featureStatus) : undefined,
q
? or(
ilike(feature.title, `%${q}%`),
Expand All @@ -105,46 +96,51 @@ export default async function ProjectFeedbackPage({ params, searchParams }: Page
: undefined
);

// Fetch features with pagination
const features = await databaseDrizzle.query.feature.findMany({
where: featureWhere,
orderBy: (f, { desc, asc }) => {
if (sort === "most-votes") return desc(f.upvotesCount);
if (sort === "least-votes") return asc(f.upvotesCount);
if (sort === "oldest") return asc(f.createdAt);
return desc(f.createdAt); // newest default
},
limit: pageSize,
offset: (currentPage - 1) * pageSize,
with: {
comments: { columns: { id: true } }, // only count for list
upvotes: {
where: (u, ops) =>
ops.or(
ops.eq(u.voterToken, session.user.id),
ops.eq(u.voterEmail, session.user.email)
),
columns: { id: true },
const [projectData, features, totalCountResult, memberships] = await Promise.all([
databaseDrizzle.query.project.findFirst({
where: eq(project.id, id),
columns: { name: true },
with: { tags: true },
}),
databaseDrizzle.query.feature.findMany({
where: featureWhere,
orderBy: (f, { desc, asc }) => {
if (sort === "most-votes") return desc(f.upvotesCount);
if (sort === "least-votes") return asc(f.upvotesCount);
if (sort === "oldest") return asc(f.createdAt);
return desc(f.createdAt);
},
limit: pageSize,
offset: (currentPage - 1) * pageSize,
with: {
comments: { columns: { id: true } },
upvotes: {
where: (u, ops) =>
ops.or(
ops.eq(u.voterToken, session.user.id),
ops.eq(u.voterEmail, session.user.email)
),
columns: { id: true },
},
tags: { with: { tag: true } },
},
tags: { with: { tag: true } },
},
});
}),
databaseDrizzle
.select({ count: sql<number>`count(*)` })
.from(feature)
.where(featureWhere)
.limit(1),
databaseDrizzle.query.usersProjects.findMany({
where: eq(usersProjects.projectId, id),
columns: { userId: true, role: true },
})
]);

if (!projectData) return notFound();

// Count total for pagination
const totalCountResult = await databaseDrizzle
.select({ count: sql<number>`count(*)` })
.from(feature)
.where(featureWhere)
.limit(1);
const totalCount = totalCountResult[0]?.count ?? 0;
const totalPages = Math.ceil(totalCount / pageSize);

// Get memberships for role checking
const memberships = await databaseDrizzle.query.usersProjects.findMany({
where: eq(usersProjects.projectId, id),
columns: { userId: true, role: true },
});

const permit = permission(memberships, session.user.id)

return (
Expand Down
9 changes: 2 additions & 7 deletions app/(dashboard)/projects/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import {
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
import { auth } from "@/lib/auth";
import { redirect } from 'next/navigation'
import { headers } from "next/headers";
import { databaseDrizzle } from "@/db";
import { UserProject } from "@/type";
import { getServerSession } from "@/lib/server/session";

export default async function ProjectLayout({
children,
Expand All @@ -19,11 +18,7 @@ export default async function ProjectLayout({
children: React.ReactNode;
params: Promise<{ id: string }>
}>) {
const {id} = await params

const session = await auth.api.getSession({
headers: await headers(),
});
const [{ id }, session] = await Promise.all([params, getServerSession()])

if (!session?.user.id) return redirect("/signin")

Expand Down
15 changes: 15 additions & 0 deletions app/(dashboard)/projects/[id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Skeleton } from "@/components/ui/skeleton"

export default function ProjectLoading() {
return (
<div className="w-full px-4 py-6 sm:px-6 lg:px-8 space-y-6">
<Skeleton className="h-10 w-64" />
<div className="grid gap-4 md:grid-cols-3">
<Skeleton className="h-28 rounded-xl" />
<Skeleton className="h-28 rounded-xl" />
<Skeleton className="h-28 rounded-xl" />
</div>
<Skeleton className="h-[420px] rounded-xl" />
</div>
)
}
Loading