Put meaningful data on each page
This commit is contained in:
@@ -1,100 +1,102 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "mysql"
|
provider = "mysql"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
relationMode = "prisma"
|
relationMode = "prisma"
|
||||||
}
|
}
|
||||||
|
|
||||||
model Account {
|
model Account {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
type String
|
type String
|
||||||
provider String
|
provider String
|
||||||
providerAccountId String
|
providerAccountId String
|
||||||
refresh_token String? @db.Text
|
refresh_token String? @db.Text
|
||||||
access_token String? @db.Text
|
access_token String? @db.Text
|
||||||
expires_at Int?
|
expires_at Int?
|
||||||
token_type String?
|
token_type String?
|
||||||
scope String?
|
scope String?
|
||||||
id_token String? @db.Text
|
id_token String? @db.Text
|
||||||
session_state String?
|
session_state String?
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([provider, providerAccountId])
|
@@unique([provider, providerAccountId])
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
sessionToken String @unique
|
sessionToken String @unique
|
||||||
userId String
|
userId String
|
||||||
expires DateTime
|
expires DateTime
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model VerificationToken {
|
model VerificationToken {
|
||||||
identifier String
|
identifier String
|
||||||
token String @unique
|
token String @unique
|
||||||
expires DateTime
|
expires DateTime
|
||||||
|
|
||||||
@@unique([identifier, token])
|
@@unique([identifier, token])
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
username String? @unique
|
||||||
email String @unique
|
name String?
|
||||||
emailVerified DateTime?
|
email String @unique
|
||||||
username String? @unique
|
emailVerified DateTime?
|
||||||
name String?
|
image String?
|
||||||
bio String?
|
bio String?
|
||||||
image String?
|
accounts Account[]
|
||||||
|
sessions Session[]
|
||||||
accounts Account[]
|
authoredProjects Project[] @relation("author")
|
||||||
sessions Session[]
|
projects Project[] @relation("members")
|
||||||
|
|
||||||
authoredProjects Project[] @relation("author")
|
|
||||||
projects Project[] @relation("members")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Project {
|
model Project {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
title String
|
||||||
|
description String
|
||||||
|
authorId String
|
||||||
|
bannerImageUrl String?
|
||||||
|
state ProjectLifecycle @default(PROPOSAL)
|
||||||
|
author User @relation("author", fields: [authorId], references: [id])
|
||||||
|
members User[] @relation("members")
|
||||||
|
previews ProjectPreview[]
|
||||||
|
|
||||||
title String
|
@@index([authorId])
|
||||||
description String
|
|
||||||
bannerImageUrl String?
|
|
||||||
|
|
||||||
authorId String
|
|
||||||
author User @relation("author", fields: [authorId], references: [id])
|
|
||||||
members User[] @relation("members")
|
|
||||||
|
|
||||||
state ProjectLifecycle @default(PROPOSAL)
|
|
||||||
previews ProjectPreview[]
|
|
||||||
|
|
||||||
@@index([authorId])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model ProjectPreview {
|
model ProjectPreview {
|
||||||
projectState ProjectLifecycle
|
projectState ProjectLifecycle
|
||||||
projectId String
|
projectId String
|
||||||
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
title String?
|
||||||
|
description String?
|
||||||
|
cardImageUrl String?
|
||||||
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
title String?
|
@@unique([projectId, projectState])
|
||||||
description String?
|
}
|
||||||
cardImageUrl String?
|
|
||||||
|
|
||||||
@@unique([projectId, projectState])
|
model members {
|
||||||
|
A String
|
||||||
|
B String
|
||||||
|
|
||||||
|
@@unique([A, B], map: "_members_AB_unique")
|
||||||
|
@@index([B], map: "_members_B_index")
|
||||||
|
@@map("_members")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ProjectLifecycle {
|
enum ProjectLifecycle {
|
||||||
PROPOSAL
|
PROPOSAL
|
||||||
IN_PROGRESS
|
IN_PROGRESS
|
||||||
REVISION
|
REVISION
|
||||||
COMPLETE
|
COMPLETE
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/components/projects/DisplayProjectCard.tsx
Normal file
15
src/components/projects/DisplayProjectCard.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Project } from "@prisma/client";
|
||||||
|
import { formatDate } from "@utils/filters";
|
||||||
|
|
||||||
|
export const DisplayProjectCard: React.FC<{ project: Project }> = ({
|
||||||
|
project,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div key={project.id} className="mb-10">
|
||||||
|
<h4 className="text-r-2xl font-medium">{project.title}</h4>
|
||||||
|
<p className="text-r-sm">{formatDate(project.createdAt)}</p>
|
||||||
|
<p className="text-r-sm">{project.state}</p>
|
||||||
|
<p className="text-r-lg">{project.description}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
18
src/components/projects/EditProjectCard.tsx
Normal file
18
src/components/projects/EditProjectCard.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import type { Project } from "@prisma/client";
|
||||||
|
import { formatDate } from "@utils/filters";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const EditProjectCard: React.FC<{ project: Project }> = ({
|
||||||
|
project,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Link href={`/projects/${project.id}`}>
|
||||||
|
<div className="mb-10 rounded border-[6px] border-fg py-2 px-3">
|
||||||
|
<h4 className="text-r-2xl font-medium">{project.title}</h4>
|
||||||
|
<p className="text-r-sm">{formatDate(project.createdAt)}</p>
|
||||||
|
<p className="text-r-sm">{project.state}</p>
|
||||||
|
<p className="text-r-lg">{project.description}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import Button from "@components/Button";
|
|
||||||
import type { Project } from "@prisma/client";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
|
||||||
|
|
||||||
export const ProjectCard: React.FC<{ project: Project }> = ({ project }) => {
|
|
||||||
return (
|
|
||||||
<div key={project.id} className="mb-10">
|
|
||||||
<h4 className="text-r-2xl font-medium">{project.title}</h4>
|
|
||||||
<p className="text-r-sm">{dayjs(project.createdAt).fromNow()}</p>
|
|
||||||
<p className="text-r-sm">{project.state}</p>
|
|
||||||
<p className="text-r-lg">{project.description}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -80,7 +80,16 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) {
|
|||||||
const session = await getServerAuthSession(ctx);
|
const session = await getServerAuthSession(ctx);
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
const providers = await getProviders();
|
// const providers = await getProviders();
|
||||||
|
const providers = {
|
||||||
|
google: {
|
||||||
|
id: "google",
|
||||||
|
name: "Google",
|
||||||
|
type: "oauth",
|
||||||
|
signinUrl: "http://localhost:3000/api/auth/signin/google",
|
||||||
|
callbackUrl: "http://localhost:3000/api/auth/callback/google",
|
||||||
|
},
|
||||||
|
};
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
providers,
|
providers,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DiscoverLayout } from "@components/layouts";
|
import { DiscoverLayout } from "@components/layouts";
|
||||||
import { api } from "@utils/api";
|
import { api } from "@utils/api";
|
||||||
import type { InferGetServerSidePropsType, NextPage } from "next";
|
import type { InferGetServerSidePropsType, NextPage } from "next";
|
||||||
import { ProjectCard } from "@components/projects/ProjectCard";
|
import { DisplayProjectCard } from "@components/projects/DisplayProjectCard";
|
||||||
import { requireAuth } from "@components/HOC/requireAuth";
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
|
||||||
const Discover: NextPage<
|
const Discover: NextPage<
|
||||||
@@ -18,7 +18,7 @@ const Discover: NextPage<
|
|||||||
<p>Loading ...</p>
|
<p>Loading ...</p>
|
||||||
) : (
|
) : (
|
||||||
data?.map((project) => (
|
data?.map((project) => (
|
||||||
<ProjectCard key={project.id} project={project} />
|
<DisplayProjectCard key={project.id} project={project} />
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</DiscoverLayout>
|
</DiscoverLayout>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
InferGetServerSidePropsType,
|
InferGetServerSidePropsType,
|
||||||
NextPage,
|
NextPage,
|
||||||
} from "next";
|
} from "next";
|
||||||
import { ProjectCard } from "@components/projects/ProjectCard";
|
import { ProjectCard } from "@components/projects/DisplayProjectCard";
|
||||||
import { requireAuth } from "@components/HOC/requireAuth";
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
|
||||||
const Discover: NextPage<
|
const Discover: NextPage<
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DiscoverLayout } from "@components/layouts";
|
import { DiscoverLayout } from "@components/layouts";
|
||||||
import { api } from "@utils/api";
|
import { api } from "@utils/api";
|
||||||
import type { InferGetServerSidePropsType, NextPage } from "next";
|
import type { InferGetServerSidePropsType, NextPage } from "next";
|
||||||
import { ProjectCard } from "@components/projects/ProjectCard";
|
import { DisplayProjectCard } from "@components/projects/DisplayProjectCard";
|
||||||
import { requireAuth } from "@components/HOC/requireAuth";
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
|
||||||
const Discover: NextPage<
|
const Discover: NextPage<
|
||||||
@@ -18,7 +18,7 @@ const Discover: NextPage<
|
|||||||
<p>Loading ...</p>
|
<p>Loading ...</p>
|
||||||
) : (
|
) : (
|
||||||
data?.map((project) => (
|
data?.map((project) => (
|
||||||
<ProjectCard key={project.id} project={project} />
|
<DisplayProjectCard key={project.id} project={project} />
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</DiscoverLayout>
|
</DiscoverLayout>
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { DiscoverLayout } from "@components/layouts";
|
import { DiscoverLayout } from "@components/layouts";
|
||||||
import { getServerAuthSession } from "@server/auth";
|
|
||||||
import { api } from "@utils/api";
|
import { api } from "@utils/api";
|
||||||
import type {
|
import type { InferGetServerSidePropsType, NextPage } from "next";
|
||||||
GetServerSideProps,
|
import { DisplayProjectCard } from "@components/projects/DisplayProjectCard";
|
||||||
GetServerSidePropsContext,
|
|
||||||
InferGetServerSidePropsType,
|
|
||||||
NextPage,
|
|
||||||
} from "next";
|
|
||||||
import { ProjectCard } from "@components/projects/ProjectCard";
|
|
||||||
import { requireAuth } from "@components/HOC/requireAuth";
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
|
||||||
const Discover: NextPage<
|
const Discover: NextPage<
|
||||||
@@ -24,7 +18,7 @@ const Discover: NextPage<
|
|||||||
<p>Loading ...</p>
|
<p>Loading ...</p>
|
||||||
) : (
|
) : (
|
||||||
data?.map((project) => (
|
data?.map((project) => (
|
||||||
<ProjectCard key={project.id} project={project} />
|
<DisplayProjectCard key={project.id} project={project} />
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</DiscoverLayout>
|
</DiscoverLayout>
|
||||||
|
|||||||
@@ -16,16 +16,24 @@ const Profile: NextPage<
|
|||||||
<h1 className="text-r-5xl font-bold text-primary">Profile</h1>
|
<h1 className="text-r-5xl font-bold text-primary">Profile</h1>
|
||||||
{data && (
|
{data && (
|
||||||
<>
|
<>
|
||||||
|
<h2 className="text-r-4xl">{data.name}</h2>
|
||||||
<p>{data.username}</p>
|
<p>{data.username}</p>
|
||||||
<p>{data.name}</p>
|
|
||||||
<p>{data.bio}</p>
|
<p>{data.bio}</p>
|
||||||
<p>
|
|
||||||
{data.projects.map((m) => (
|
<h3 className="text-r-3xl">My Projects</h3>
|
||||||
<>
|
{data.authoredProjects.map((m) => (
|
||||||
<p>{m.title}</p>
|
<>
|
||||||
</>
|
<p>{m.title}</p>
|
||||||
))}
|
<p>{m.description}</p>
|
||||||
</p>
|
</>
|
||||||
|
))}
|
||||||
|
<h3 className="text-r-3xl">Projects I{"'"}ve Worked On</h3>
|
||||||
|
{data.projects.map((m) => (
|
||||||
|
<>
|
||||||
|
<p>{m.title}</p>
|
||||||
|
<p>{m.description}</p>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
24
src/pages/projects/[projectId].tsx
Normal file
24
src/pages/projects/[projectId].tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
import { MainLayout } from "@components/layouts";
|
||||||
|
import { api } from "@utils/api";
|
||||||
|
import type { InferGetServerSidePropsType, NextPage } from "next";
|
||||||
|
|
||||||
|
const SpecificProject: NextPage<
|
||||||
|
InferGetServerSidePropsType<typeof getServerSideProps>
|
||||||
|
> = ({ projectId }) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const { data } = api.projects.getProjectById.useQuery({ projectId });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainLayout>
|
||||||
|
<p>{data?.id}</p>
|
||||||
|
</MainLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpecificProject;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/require-await
|
||||||
|
export const getServerSideProps = requireAuth(async (ctx) => {
|
||||||
|
return { props: { projectId: ctx.params?.projectId } };
|
||||||
|
});
|
||||||
@@ -1,11 +1,28 @@
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { MainLayout } from "@components/layouts";
|
import { MainLayout } from "@components/layouts";
|
||||||
import { requireAuth } from "@components/HOC/requireAuth";
|
import { requireAuth } from "@components/HOC/requireAuth";
|
||||||
|
import { api } from "@utils/api";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { EditProjectCard } from "@components/projects/EditProjectCard";
|
||||||
|
|
||||||
const Projects: NextPage = () => {
|
const Projects: NextPage = () => {
|
||||||
|
const { data } = api.projects.getAllByCurrentUser.useQuery();
|
||||||
|
|
||||||
|
const sortedProjects = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
|
||||||
|
const projects = data.projects.concat(data.authoredProjects);
|
||||||
|
return projects.sort(
|
||||||
|
(a, b) => b.createdAt.getTime() - a.createdAt.getTime()
|
||||||
|
);
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<h1 className="text-r-5xl font-bold text-primary">Projects</h1>
|
<h1 className="text-r-5xl font-bold text-primary">Projects</h1>
|
||||||
|
{sortedProjects.map((p) => (
|
||||||
|
<EditProjectCard key={p.id} project={p} />
|
||||||
|
))}
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const profileRouter = createTRPCRouter({
|
|||||||
name: true,
|
name: true,
|
||||||
bio: true,
|
bio: true,
|
||||||
image: true,
|
image: true,
|
||||||
|
authoredProjects: true,
|
||||||
projects: true,
|
projects: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { proposalSchema } from "@utils/constants/schema/project";
|
import { proposalSchema } from "@utils/constants/schema/project";
|
||||||
import { Project, ProjectLifecycle } from "@prisma/client";
|
import { ProjectLifecycle } from "@prisma/client";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createTRPCRouter, publicProcedure, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, publicProcedure, protectedProcedure } from "../trpc";
|
||||||
@@ -11,6 +11,12 @@ export const projectsRouter = createTRPCRouter({
|
|||||||
orderBy: [{ createdAt: "desc" }],
|
orderBy: [{ createdAt: "desc" }],
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
getAllByCurrentUser: protectedProcedure.query(({ ctx }) => {
|
||||||
|
return ctx.prisma.user.findUnique({
|
||||||
|
where: { id: ctx.session.user.id },
|
||||||
|
select: { authoredProjects: true, projects: true },
|
||||||
|
});
|
||||||
|
}),
|
||||||
getAllByUserId: publicProcedure
|
getAllByUserId: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
@@ -36,6 +42,17 @@ export const projectsRouter = createTRPCRouter({
|
|||||||
orderBy: [{ createdAt: "desc" }],
|
orderBy: [{ createdAt: "desc" }],
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
getProjectById: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
projectId: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.query(({ ctx, input }) => {
|
||||||
|
return ctx.prisma.project.findUnique({
|
||||||
|
where: { id: input.projectId },
|
||||||
|
});
|
||||||
|
}),
|
||||||
createProposal: protectedProcedure
|
createProposal: protectedProcedure
|
||||||
.input(proposalSchema)
|
.input(proposalSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
|
|
||||||
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
export const formatDate = (date: Date) => dayjs(date).fromNow();
|
||||||
|
|
||||||
export const getQueryOrDefault = (
|
export const getQueryOrDefault = (
|
||||||
value: string | string[] | undefined,
|
value: string | string[] | undefined,
|
||||||
fallback: string
|
fallback: string
|
||||||
|
|||||||
Reference in New Issue
Block a user