Skip to content
Merged
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
48 changes: 41 additions & 7 deletions src/actions/invite-actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from "zod";
import crypto from "crypto";
import { db } from "~/server/db";
import { invite } from "~/server/db/schema";
import { invites, usersToProjects } from "~/server/db/schema";
import { and, eq } from "drizzle-orm";
import { differenceInDays } from "date-fns";

Expand All @@ -25,11 +25,11 @@ export async function createInvite(userId: string, projectId: string) {
};
const currentInvites = await db
.select()
.from(invite)
.from(invites)
.where(
and(
eq(invite.userId, data.userId),
eq(invite.projectId, data.projectId),
eq(invites.userId, data.userId),
eq(invites.projectId, data.projectId),
),
);

Expand All @@ -39,12 +39,46 @@ export async function createInvite(userId: string, projectId: string) {
if (age < 5) {
return currentInvite.token;
}
await db.delete(invite).where(eq(invite.id, currentInvite.id));
await db.delete(invites).where(eq(invites.id, currentInvite.id));
}
const hash = crypto.createHash("sha256");
const stringified = JSON.stringify(newInvite);
hash.update(stringified);
const token = hash.digest("base64");
await db.insert(invite).values({ ...newInvite, token, date: date });
const token = hash.digest("base64").replace("/", "-");
await db.insert(invites).values({ ...newInvite, token, date: date });
return token;
}

export async function joinProject(token: string, userId: string) {
const requestInvite = await db
.selectDistinct()
.from(invites)
.where(eq(invites.token, token));
if (!requestInvite || requestInvite.length === 0) {
return { success: false, message: "Invalid invite link" };
}
const inviteData = requestInvite[0];
if (!inviteData) {
return { success: false, message: "Invalid invite link" };
}
const date = new Date();
const age = differenceInDays(date, inviteData.date);

if (age > 5) {
return { success: false, message: "Invite link expired" };
}
try {
await db
.insert(usersToProjects)
.values({ userId: userId, projectId: inviteData.projectId });
return {
success: true,
message: "You have successfully joined this project",
};
} catch (e) {
return {
success: false,
message: "You have already joined this project",
};
}
}
30 changes: 21 additions & 9 deletions src/actions/project-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,34 @@
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { db } from "~/server/db";
import { project, insertProjectSchema } from "~/server/db/schema";
import { projects, insertProjectSchema } from "~/server/db/schema";
import { type Project, type NewProject } from "~/server/db/schema";

export async function createProject(data: NewProject) {
try {
const newProject: NewProject = insertProjectSchema.parse(data);
await db.insert(project).values(newProject);
await db.insert(projects).values(newProject);
revalidatePath("/");
} catch (error) {
if (error instanceof Error) console.log(error.stack);
}
}

export async function getAllProjects() {
export async function getAllProjects(userId: string) {
try {
const allProjects: Project[] = await db.select().from(project);
const projectsQuery = await db.query.users.findMany({
where: (user) => eq(user.userId, userId),
with: {
usersToProjects: {
with: {
project: true,
},
},
},
});
const allProjects = projectsQuery.flatMap((userToProject) =>
userToProject.usersToProjects.map((up) => up.project),
);
return allProjects;
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand All @@ -27,19 +39,19 @@ export async function getAllProjects() {

export async function getProject(id: number) {
try {
const projects: Project[] = await db
const allProjects: Project[] = await db
.select()
.from(project)
.where(eq(project.id, id));
return projects[0];
.from(projects)
.where(eq(projects.id, id));
return allProjects[0];
} catch (error) {
if (error instanceof Error) console.log(error.stack);
}
}

export async function deleteProject(id: number) {
try {
await db.delete(project).where(eq(project.id, id));
await db.delete(projects).where(eq(projects.id, id));
revalidatePath("/");
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand Down
23 changes: 11 additions & 12 deletions src/actions/task-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { db } from "~/server/db";
import { task, insertTaskSchema } from "~/server/db/schema";
import { tasks, insertTaskSchema__required } from "~/server/db/schema";
import { type Task, type NewTask } from "~/server/db/schema";

export async function createTask(data: NewTask) {
try {
const newTask: NewTask = insertTaskSchema.parse(data);
await db.insert(task).values(newTask);
const newTask = insertTaskSchema__required.parse(data);
await db.insert(tasks).values(newTask);
revalidatePath("/");
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand All @@ -18,7 +18,7 @@ export async function createTask(data: NewTask) {

export async function getAllTasks() {
try {
const allTasks: Task[] = await db.select().from(task);
const allTasks: Task[] = await db.select().from(tasks);
return allTasks;
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand All @@ -27,19 +27,19 @@ export async function getAllTasks() {

export async function getTasksFromProject(projectId: number) {
try {
const tasks: Task[] = await db
const allTasks: Task[] = await db
.select()
.from(task)
.where(eq(task.projectId, projectId));
return tasks;
.from(tasks)
.where(eq(tasks.projectId, projectId));
return allTasks;
} catch (error) {
if (error instanceof Error) console.log(error.stack);
}
}

export async function deleteTask(id: number) {
try {
await db.delete(task).where(eq(task.id, id));
await db.delete(tasks).where(eq(tasks.id, id));
revalidatePath("/");
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand All @@ -48,9 +48,8 @@ export async function deleteTask(id: number) {

export async function updateTask(id: number, data: NewTask) {
try {
const updatedTaskData: NewTask = insertTaskSchema.parse(data);
await db.update(task).set(updatedTaskData).where(eq(task.id, id));
// console.log("updated task");
const updatedTaskData: NewTask = insertTaskSchema__required.parse(data);
await db.update(tasks).set(updatedTaskData).where(eq(tasks.id, id));
revalidatePath("/");
} catch (error) {
if (error instanceof Error) console.log(error.stack);
Expand Down
23 changes: 23 additions & 0 deletions src/app/(application)/join/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { auth } from "@clerk/nextjs";
import { joinProject } from "~/actions/invite-actions";

type Params = {
params: {
token: string;
};
};

export default async function ProjectSettingsInvite({
params: { token },
}: Params) {
const decodedToken = decodeURIComponent(token);
const { userId }: { userId: string | null } = auth();
if (!userId) return null;
const result = await joinProject(decodedToken, userId);

return (
<div className="container flex flex-col pt-4">
<p>{result.message}</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { auth } from "@clerk/nextjs";
import dynamic from "next/dynamic";
import { createInvite } from "~/actions/invite-actions";
//import InviteLink from "~/components/invite/invite-link";
const InviteLink = dynamic(() => import("~/components/invite/invite-link"), {
ssr: false,
});
Expand Down
8 changes: 4 additions & 4 deletions src/app/(application)/tasks/ai-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Textarea } from "~/components/ui/textarea";
import { Label } from "~/components/ui/label";
import { Bot, ChevronRight, Loader2 } from "lucide-react";
import { DialogClose } from "@radix-ui/react-dialog";
import { type Task, insertTaskSchema } from "~/server/db/schema";
import { type Task, selectTaskSchema } from "~/server/db/schema";
import { createTask } from "~/actions/task-actions";

type AiTask = { [K in keyof Omit<Task, "id">]?: Task[K] };
Expand Down Expand Up @@ -106,7 +106,7 @@ const AiDialog = ({ dispatch }: Props) => {
const [isPending, startTransition] = useTransition();
async function handleAccept() {
try {
const validatedTask = insertTaskSchema.parse(taskObject);
const validatedTask = selectTaskSchema.parse(taskObject);
dispatch({
type: "ADD",
payload: { ...validatedTask, id: Math.random() },
Expand Down Expand Up @@ -156,7 +156,7 @@ const AiDialog = ({ dispatch }: Props) => {
<div>
{taskObject ? (
<ul>
{insertTaskSchema.safeParse(taskObject)
{selectTaskSchema.safeParse(taskObject)
.success === false ? (
<div className="flex items-center gap-1">
<p>Creating Task</p>
Expand Down Expand Up @@ -233,7 +233,7 @@ const AiDialog = ({ dispatch }: Props) => {
}
disabled={
isPending ||
insertTaskSchema.safeParse(taskObject)
selectTaskSchema.safeParse(taskObject)
.success === false
}
>
Expand Down
18 changes: 7 additions & 11 deletions src/app/api/clerk/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import { headers } from "next/headers";
import { Webhook as svixWebhook } from "svix";
import { env } from "~/env.mjs";
import { db } from "~/server/db";
import {
type NewUserInfo,
insertUserInfoSchema,
userInfo,
} from "~/server/db/schema";
import { type NewUser, insertUserSchema, users } from "~/server/db/schema";

const webhookSecret = env.CLERK_WEBHOOK_SECRET;

Expand Down Expand Up @@ -50,20 +46,20 @@ async function onUserCreated(payload: UserWebhookEvent) {
}

async function helperCreateUser(userId: string) {
const data: NewUserInfo = {
const data: NewUser = {
userId,
};

const newUserInfo: NewUserInfo = insertUserInfoSchema.parse(data);
await db.insert(userInfo).values(newUserInfo);
const newUser = insertUserSchema.parse(data);
await db.insert(users).values(newUser);
}

async function onUserDeleted(payload: UserWebhookEvent) {
const userId = payload.data.id;
if (!userId) {
throw new Error("No user ID provided");
}
await db.delete(userInfo).where(eq(userInfo.userId, userId));
await db.delete(users).where(eq(users.userId, userId));
}

async function onSessionCreated(payload: SessionWebhookEvent) {
Expand All @@ -72,8 +68,8 @@ async function onSessionCreated(payload: SessionWebhookEvent) {
// check if the user exists in our database
const user = await db
.selectDistinct()
.from(userInfo)
.where(eq(userInfo.userId, userId));
.from(users)
.where(eq(users.userId, userId));

// if not, create a new user
if (user.length === 0) {
Expand Down
5 changes: 4 additions & 1 deletion src/components/layout/navbar/project-list.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"use server";

import { auth } from "@clerk/nextjs";
import React from "react";
import { getAllProjects } from "~/actions/project-actions";

import ProjectMenuItem from "~/components/layout/navbar/project-menu-item";

async function ProjectList() {
const projects = await getAllProjects();
const { userId }: { userId: string | null } = auth();
if (!userId) return null;
const projects = await getAllProjects(userId);
if (!projects) return <p>Error</p>;

return <ProjectMenuItem projects={projects} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import React from "react";
import { getAllProjects } from "~/actions/project-actions";
import ProjectCombobox from "./project-combobox";
import { auth } from "@clerk/nextjs";

type Props = {
projectId: string;
};

async function SelectProject({ projectId }: Props) {
const projects = await getAllProjects();
const { userId }: { userId: string | null } = auth();
if (!userId) return null;
const projects = await getAllProjects(userId);
if (!projects) return <p>Error</p>;

return <ProjectCombobox projects={projects} projectId={projectId} />;
Expand Down
Loading