[] = [
{
- id: "team_details",
- header: "Team",
- accessorFn: (row) => `${row.displayName} ${row.name}`,
- cell: (info) => (
-
-
- {info.row.original.displayName}
-
-
- @{info.row.original.name}
-
-
- ),
+ accessorKey: "name",
+ header: "Name",
},
{
accessorKey: "role",
@@ -55,13 +44,8 @@ export const teamColumns: ColumnDef[] = [
cell: ({ row }) => {
const { data, isPending } = authClient.useSession();
const { mutateAsync: leaveTeam } = useMutation({
- mutationFn: ({
- teamId,
- memberId,
- }: {
- teamId: string;
- memberId: string;
- }) => TeamController.removeMember(teamId, memberId),
+ mutationFn: ({ teamId, memberId }: { teamId: string; memberId: string }) =>
+ TeamController.removeMember(teamId, memberId),
onSuccess: (_, { teamId }) => {
queryClient.setQueryData(["teams"], (prevData) =>
prevData ? prevData.filter((team) => team.id !== teamId) : [],
@@ -85,16 +69,14 @@ export const teamColumns: ColumnDef[] = [
- navigator.clipboard.writeText(team.id)}
- >
+ navigator.clipboard.writeText(team.id)}>
Copy Team ID
{
const toastId = toast.loading("Leaving team...");
try {
@@ -121,10 +103,7 @@ export const teamColumns: ColumnDef[] = [
submitButtonVariant={"destructive"}
cancelButtonVariant={"outline"}
trigger={
- e.preventDefault()}
- >
+ e.preventDefault()}>
Leave Team
diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
index 654299c..4777c6e 100644
--- a/src/app/dashboard/page.tsx
+++ b/src/app/dashboard/page.tsx
@@ -34,7 +34,7 @@ export default function Page() {
columns={teamColumns}
emptyString="No Teams"
searchBoxPlaceholder="Search teams"
- searchBoxTarget="team_details"
+ searchBoxTarget="name"
loading={isLoading}
loadingString="Loading..."
actionComponent={}
diff --git a/src/controllers/TeamController.ts b/src/controllers/TeamController.ts
index 68a2c09..a34b0ee 100644
--- a/src/controllers/TeamController.ts
+++ b/src/controllers/TeamController.ts
@@ -16,13 +16,10 @@ export const TeamController = {
resolve: (slug: string) =>
fetcher(`/api/teams/resolve-slug/${slug}`),
- changeName: (
- teamId: string,
- newName: { name?: string; displayName?: string },
- ) =>
- fetcher(`/api/teams/${teamId}`, {
+ changeName: (teamId: string, newName: string) =>
+ fetcher(`/api/teams/${teamId}`, {
method: "PATCH",
- body: JSON.stringify(newName),
+ body: JSON.stringify({ name: newName }),
}),
removeMember: (teamId: string, memberId: string) =>
diff --git a/src/db/schema/team.ts b/src/db/schema/team.ts
index 2fe1fbc..1fc03e7 100644
--- a/src/db/schema/team.ts
+++ b/src/db/schema/team.ts
@@ -4,10 +4,9 @@ import { user } from "./user";
export const team = sqliteTable("team", {
id: text("id").primaryKey(),
- slug: text("slug").notNull().unique(),
- displayName: text("display_name").notNull(),
name: text("name").notNull(),
+ slug: text("slug").notNull().unique(),
ownerId: text("owner_id")
.notNull()
diff --git a/src/hooks/useTeam.ts b/src/hooks/useTeam.ts
index c6796e7..8ce1885 100644
--- a/src/hooks/useTeam.ts
+++ b/src/hooks/useTeam.ts
@@ -36,7 +36,6 @@ export function useTeams() {
export function useTeamMutations() {
const queryClient = useQueryClient();
- const router = useRouter();
const createTeam = useMutation({
mutationFn: (name: string) => TeamController.create(name),
@@ -64,46 +63,15 @@ export function useTeamMutations() {
});
const renameTeam = useMutation({
- mutationFn: ({
- teamId,
- payload,
- }: {
- teamId: string;
- payload: { name?: string; displayName?: string };
- }) => TeamController.changeName(teamId, payload),
+ mutationFn: ({ teamId, newName }: { teamId: string; newName: string }) =>
+ TeamController.changeName(teamId, newName),
onSuccess: (newTeam, variables) => {
- const cachedTeam = queryClient.getQueryData(["teams"]);
- const oldTeam = cachedTeam?.find((team) => team.id === variables.teamId);
- const oldSlug = oldTeam?.slug;
-
- // Update the teams list
queryClient.setQueryData(["teams"], (prevData) => {
- if (!prevData) return prevData;
- return prevData.map((team) =>
- team.id === newTeam.id ? { ...team, ...newTeam } : team,
- );
+ if (!prevData) return [newTeam];
+ return prevData.map((t) => (t.id == newTeam.id ? newTeam : t));
});
- const pathname = window.location.pathname;
- const relativePath = pathname
- .split("/dashboard/")[1]
- .split("/")
- .slice(1)
- .join("/");
- router.replace(`/dashboard/${newTeam.slug}/${relativePath}`);
-
- if (oldSlug && oldTeam && oldSlug !== newTeam.slug) {
- queryClient.removeQueries({ queryKey: ["team", oldSlug] });
- queryClient.setQueryData(["team", newTeam.slug], {
- ...oldTeam,
- ...newTeam,
- });
- } else if (oldSlug) {
- queryClient.setQueryData(["team", oldSlug], (oldTeam) => {
- if (!oldTeam) return oldTeam;
- return { ...oldTeam, ...newTeam };
- });
- }
+ queryClient.setQueryData(["team", newTeam.slug], () => newTeam);
},
});
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index 8444221..589fcf9 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -103,13 +103,7 @@ export const auth = betterAuth({
user: {
create: {
after: async (user) => {
- const team = await TeamService.CreateTeam(
- user.id,
- `user-${user.id.slice(-6)}'s team`,
- );
- await TeamService.ChangeTeamName(user.id, team.id, {
- displayName: "Personal Team",
- });
+ await TeamService.CreateTeam(user.id, `${user.email}'s Teams`);
},
},
},
diff --git a/src/lib/types/team-types.ts b/src/lib/types/team-types.ts
index 3332365..a02c65e 100644
--- a/src/lib/types/team-types.ts
+++ b/src/lib/types/team-types.ts
@@ -1,5 +1,6 @@
import { user, team_member, team } from "@/src/db/schema";
import { InferDrizzleSelect } from "../utils";
+import z from "zod";
export type TeamRole = typeof team_member.$inferSelect.role;
@@ -15,7 +16,6 @@ export type TeamMember = InferDrizzleSelect;
export const TeamSelect = {
id: team.id,
name: team.name,
- displayName: team.displayName,
slug: team.slug,
ownerId: team.ownerId,
createdAt: team.createdAt,
@@ -28,3 +28,18 @@ export const UserTeamSelect = {
role: team_member.role,
};
export type UserTeam = InferDrizzleSelect;
+
+
+export const CreateTeamSchema = z.object({
+ name: z
+ .string()
+ .min(3, { error: "Team name must be at least 3 characters" })
+ .max(32, { error: "Team name must be at most 32 characters" }),
+});
+
+export const RenameTeamSchema = z.object({
+ name: z
+ .string()
+ .min(3, { error: "Name must be at least 3 characters" })
+ .max(32, { error: "Name must be at most 32 characters" }),
+});
diff --git a/src/services/TeamService.ts b/src/services/TeamService.ts
index 23b4830..fa4a944 100644
--- a/src/services/TeamService.ts
+++ b/src/services/TeamService.ts
@@ -119,7 +119,6 @@ export const TeamService = {
.values({
id: teamId,
name: teamName,
- displayName: teamName,
slug,
ownerId: actorId,
})
@@ -225,40 +224,37 @@ export const TeamService = {
async ChangeTeamName(
actorId: string,
teamId: string,
- newName: { displayName?: string; name?: string },
- ): Promise {
+ newName: string,
+ ): Promise {
const role = await this.GetTeamUserRole(actorId, teamId);
if (!hasPermission(role, "ChangeTeamName")) {
throw AccessDenied;
}
- const updatePayload: Partial = {};
-
- if (newName.displayName) {
- updatePayload.displayName = newName.displayName;
- }
-
- if (newName.name) {
- updatePayload.name = newName.name;
- }
-
- if (Object.keys(updatePayload).length < 1) {
- return;
- }
-
for (let i = 0; i < 3; i++) {
- if (updatePayload.name) {
- updatePayload.slug = this.CreateSlugFromName(updatePayload.name);
+ let slug;
+ try {
+ slug = this.CreateSlugFromName(newName);
+ } catch (error) {
+ throw error;
}
+
try {
- const [result] = await db
+ await db
.update(team)
- .set(updatePayload)
- .where(eq(team.id, teamId))
- .returning(TeamSelect);
+ .set({ slug, name: newName })
+ .where(eq(team.id, teamId));
+
+ const [result] = await db
+ .select(UserTeamSelect)
+ .from(team)
+ .innerJoin(team_member, eq(team.id, team_member.teamId))
+ .where(and(eq(team.id, teamId), eq(team_member.userId, actorId)));
+
return result;
} catch (error) {
+ console.log(error);
if (error instanceof Error) {
if (error.message.includes("SQLITE_CONSTRAINT_UNIQUE")) {
continue;
@@ -267,6 +263,7 @@ export const TeamService = {
throw DatabaseError;
}
}
+ throw InvalidSlug;
},
async GetTeamBySlug(