chore: move interactions to repo directory
This commit is contained in:
10129
package-lock.json
generated
Normal file
10129
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,7 @@
|
|||||||
"postcss": "^8.4.34",
|
"postcss": "^8.4.34",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||||
"supabase": "^1.153.4",
|
"supabase": "^1.187.3",
|
||||||
"tailwind": "^4.0.0",
|
"tailwind": "^4.0.0",
|
||||||
"tailwind-scrollbar": "^3.0.5",
|
"tailwind-scrollbar": "^3.0.5",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
|
|||||||
@@ -8,18 +8,15 @@ import {
|
|||||||
|
|
||||||
import { env } from "~/env.mjs";
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
|
import * as userRepo from "~/server/db/repository/user.repo";
|
||||||
|
|
||||||
export const authRouter = createTRPCRouter({
|
export const authRouter = createTRPCRouter({
|
||||||
getUser: protectedProcedure.query(async ({ ctx }) => {
|
getUser: protectedProcedure.query(async ({ ctx }) => {
|
||||||
if (!ctx.user?.id) return;
|
if (!ctx.user?.id) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const result = await userRepo.getById(ctx.db, ctx.user.id);
|
||||||
.from("user")
|
|
||||||
.select(`id, name, email`)
|
|
||||||
.eq("id", ctx.user.id)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
loginWithEmail: publicProcedure
|
loginWithEmail: publicProcedure
|
||||||
.input(z.object({ email: z.string() }))
|
.input(z.object({ email: z.string() }))
|
||||||
|
|||||||
@@ -1,88 +1,33 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
import * as boardRepo from "~/server/db/repository/board.repo";
|
||||||
|
import * as cardRepo from "~/server/db/repository/card.repo";
|
||||||
|
import * as listRepo from "~/server/db/repository/list.repo";
|
||||||
|
import * as workspaceRepo from "~/server/db/repository/workspace.repo";
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: protectedProcedure
|
all: protectedProcedure
|
||||||
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const workspace = await ctx.db
|
const workspace = await workspaceRepo.getByPublicId(
|
||||||
.from("workspace")
|
ctx.db,
|
||||||
.select(`id`)
|
input.workspacePublicId,
|
||||||
.eq("publicId", input.workspacePublicId)
|
);
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const result = boardRepo.getAllByWorkspaceId(ctx.db, workspace.id);
|
||||||
.from("board")
|
|
||||||
.select(`publicId, name`)
|
|
||||||
.is("deletedAt", null)
|
|
||||||
.eq("workspaceId", workspace.data.id);
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
byId: protectedProcedure
|
byId: protectedProcedure
|
||||||
.input(z.object({ id: z.string().min(12) }))
|
.input(z.object({ boardPublicId: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const result = await boardRepo.getByPublicId(ctx.db, input.boardPublicId);
|
||||||
.from("board")
|
|
||||||
.select(
|
|
||||||
`
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
workspace (
|
|
||||||
publicId,
|
|
||||||
members:workspace_members (
|
|
||||||
publicId,
|
|
||||||
user (
|
|
||||||
name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
colourCode
|
|
||||||
),
|
|
||||||
lists:list (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
boardId,
|
|
||||||
index,
|
|
||||||
cards:card (
|
|
||||||
publicId,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
listId,
|
|
||||||
index,
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
colourCode
|
|
||||||
),
|
|
||||||
members:workspace_members (
|
|
||||||
publicId,
|
|
||||||
user (
|
|
||||||
name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.eq("publicId", input.id)
|
|
||||||
.is("deletedAt", null)
|
|
||||||
.is("lists.deletedAt", null)
|
|
||||||
.is("lists.cards.deletedAt", null)
|
|
||||||
.order("index", { foreignTable: "list", ascending: true })
|
|
||||||
.order("index", { foreignTable: "list.card", ascending: true })
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -96,41 +41,35 @@ export const boardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const workspace = await workspaceRepo.getByPublicId(
|
||||||
.from("workspace")
|
ctx.db,
|
||||||
.select(`id`)
|
input.workspacePublicId,
|
||||||
.eq("publicId", input.workspacePublicId)
|
);
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const result = await boardRepo.create(ctx.db, {
|
||||||
.from("board")
|
name: input.name,
|
||||||
.insert({
|
createdBy: userId,
|
||||||
publicId: generateUID(),
|
workspaceId: workspace.id,
|
||||||
name: input.name,
|
});
|
||||||
createdBy: userId,
|
|
||||||
workspaceId: workspace.data.id,
|
|
||||||
})
|
|
||||||
.select(`publicId, name`);
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
boardId: z.string().min(12),
|
boardPublicId: z.string().min(12),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const result = await boardRepo.update(ctx.db, {
|
||||||
.from("board")
|
name: input.name,
|
||||||
.update({ name: input.name })
|
boardPublicId: input.boardPublicId,
|
||||||
.eq("publicId", input.boardId);
|
});
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
delete: protectedProcedure
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -141,37 +80,34 @@ export const boardRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
const board = await ctx.db
|
const board = await boardRepo.getWithListIdsByPublicId(
|
||||||
.from("board")
|
ctx.db,
|
||||||
.select(`id, lists:list (id)`)
|
input.boardPublicId,
|
||||||
.eq("publicId", input.boardPublicId)
|
);
|
||||||
.limit(1)
|
if (!board || !userId) return;
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!board.data) return;
|
const listIds = board.lists.map((list) => list.id);
|
||||||
|
|
||||||
const listIds = board.data.lists.map((list) => list.id);
|
|
||||||
|
|
||||||
const deletedAt = new Date().toISOString();
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
await ctx.db
|
await boardRepo.destroy(ctx.db, {
|
||||||
.from("board")
|
boardId: board.id,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
deletedAt,
|
||||||
.eq("id", board.data.id)
|
deletedBy: userId,
|
||||||
.is("deletedAt", null);
|
});
|
||||||
|
|
||||||
if (listIds.length) {
|
if (listIds.length) {
|
||||||
await ctx.db
|
await listRepo.destroyAllByBoardId(ctx.db, {
|
||||||
.from("list")
|
boardId: board.id,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
deletedAt,
|
||||||
.eq("boardId", board.data.id)
|
deletedBy: userId,
|
||||||
.is("deletedAt", null);
|
});
|
||||||
|
|
||||||
await ctx.db
|
await cardRepo.destroyAllByListIds(ctx.db, {
|
||||||
.from("card")
|
listIds,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
deletedAt,
|
||||||
.in("listId", listIds)
|
deletedBy: userId,
|
||||||
.is("deletedAt", null);
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
import * as cardRepo from "~/server/db/repository/card.repo";
|
||||||
|
import * as labelRepo from "~/server/db/repository/label.repo";
|
||||||
|
import * as listRepo from "~/server/db/repository/list.repo";
|
||||||
|
import * as workspaceRepo from "~/server/db/repository/workspace.repo";
|
||||||
|
|
||||||
export const cardRouter = createTRPCRouter({
|
export const cardRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
listPublicId: z.string().min(12),
|
listPublicId: z.string().min(12),
|
||||||
labelsPublicIds: z.array(z.string().min(12)),
|
labelPublicIds: z.array(z.string().min(12)),
|
||||||
memberPublicIds: z.array(z.string().min(12)),
|
memberPublicIds: z.array(z.string().min(12)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -18,63 +22,57 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const list = await ctx.db
|
const list = await listRepo.getWithCardsByPublicId(
|
||||||
.from("list")
|
ctx.db,
|
||||||
.select(`id, cards:card (index)`)
|
input.listPublicId,
|
||||||
.eq("publicId", input.listPublicId)
|
);
|
||||||
.order("index", { foreignTable: "card", ascending: false })
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!list.data?.id) return;
|
if (!list?.id) return;
|
||||||
|
|
||||||
const latestCard = list.data.cards.length && list.data.cards[0];
|
const latestCard = list.cards.length && list.cards[0];
|
||||||
|
|
||||||
const newCard = await ctx.db
|
const newCard = await cardRepo.create(ctx.db, {
|
||||||
.from("card")
|
title: input.title,
|
||||||
.insert({
|
createdBy: userId,
|
||||||
publicId: generateUID(),
|
listId: list.id,
|
||||||
title: input.title,
|
index: latestCard ? latestCard.index + 1 : 0,
|
||||||
createdBy: userId,
|
});
|
||||||
listId: list.data.id,
|
|
||||||
index: latestCard ? latestCard.index + 1 : 0,
|
|
||||||
})
|
|
||||||
.select(`id`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const newCardId = newCard.data?.id;
|
const newCardId = newCard?.id;
|
||||||
|
|
||||||
if (newCardId && input.labelsPublicIds.length) {
|
if (newCardId && input.labelPublicIds.length) {
|
||||||
const labels = await ctx.db
|
const labels = await labelRepo.getAllByPublicIds(
|
||||||
.from("label")
|
ctx.db,
|
||||||
.select(`id`)
|
input.labelPublicIds,
|
||||||
.eq("publicId", input.labelsPublicIds);
|
);
|
||||||
|
|
||||||
if (!labels.data?.length) return;
|
if (!labels?.length) return;
|
||||||
|
|
||||||
const labelsInsert = labels.data.map((label) => ({
|
const labelsInsert = labels.map((label) => ({
|
||||||
cardId: newCardId,
|
cardId: newCardId,
|
||||||
labelId: label.id,
|
labelId: label.id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await ctx.db.from("_card_labels").insert(labelsInsert);
|
await cardRepo.bulkCreateCardLabelRelationships(ctx.db, labelsInsert);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newCardId && input.memberPublicIds.length) {
|
if (newCardId && input.memberPublicIds.length) {
|
||||||
const members = await ctx.db
|
const members = await workspaceRepo.getAllMembersByPublicIds(
|
||||||
.from("workspace_members")
|
ctx.db,
|
||||||
.select(`id`)
|
input.memberPublicIds,
|
||||||
.eq("publicId", input.memberPublicIds);
|
);
|
||||||
|
|
||||||
if (!members.data?.length) return;
|
if (!members?.length) return;
|
||||||
|
|
||||||
const membersInsert = members.data.map((member) => ({
|
const membersInsert = members.map((member) => ({
|
||||||
cardId: newCardId,
|
cardId: newCardId,
|
||||||
workspaceMemberId: member.id,
|
workspaceMemberId: member.id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await ctx.db.from("_card_workspace_members").insert(membersInsert);
|
await cardRepo.bulkCreateCardWorkspaceMemberRelationships(
|
||||||
|
ctx.db,
|
||||||
|
membersInsert,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newCard;
|
return newCard;
|
||||||
@@ -91,44 +89,25 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await cardRepo.getByPublicId(ctx.db, input.cardPublicId);
|
||||||
.from("card")
|
const label = await labelRepo.getByPublicId(ctx.db, input.labelPublicId);
|
||||||
.select(`id`)
|
|
||||||
.eq("publicId", input.cardPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const label = await ctx.db
|
if (!card || !label) return;
|
||||||
.from("label")
|
|
||||||
.select(`id`)
|
|
||||||
.eq("publicId", input.labelPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data || !label.data) return;
|
const cardLabelIds = { cardId: card.id, labelId: label.id };
|
||||||
|
|
||||||
const existingLabel = await ctx.db
|
const existingLabel = await cardRepo.getCardLabelRelationship(
|
||||||
.from("_card_labels")
|
ctx.db,
|
||||||
.select()
|
cardLabelIds,
|
||||||
.eq("cardId", card.data.id)
|
);
|
||||||
.eq("labelId", label.data.id)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (existingLabel.data) {
|
if (existingLabel) {
|
||||||
await ctx.db
|
await cardRepo.destroyCardLabelRelationship(ctx.db, cardLabelIds);
|
||||||
.from("_card_labels")
|
|
||||||
.delete()
|
|
||||||
.eq("cardId", card.data.id)
|
|
||||||
.eq("labelId", label.data.id);
|
|
||||||
|
|
||||||
return { newLabel: false };
|
return { newLabel: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.db.from("_card_labels").insert({
|
await cardRepo.createCardLabelRelationship(ctx.db, cardLabelIds);
|
||||||
cardId: card.data.id,
|
|
||||||
labelId: label.data.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { newLabel: true };
|
return { newLabel: true };
|
||||||
}),
|
}),
|
||||||
@@ -144,105 +123,40 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await cardRepo.getByPublicId(ctx.db, input.cardPublicId);
|
||||||
.from("card")
|
const member = await workspaceRepo.getMemberByPublicId(
|
||||||
.select(`id`)
|
ctx.db,
|
||||||
.eq("publicId", input.cardPublicId)
|
input.workspaceMemberPublicId,
|
||||||
.limit(1)
|
);
|
||||||
.single();
|
|
||||||
|
|
||||||
const member = await ctx.db
|
if (!card || !member) return;
|
||||||
.from("workspace_members")
|
|
||||||
.select(`id`)
|
|
||||||
.eq("publicId", input.workspaceMemberPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data || !member.data) return;
|
const cardMemberIds = { cardId: card.id, memberId: member.id };
|
||||||
|
|
||||||
const existingMember = await ctx.db
|
const existingMember = await cardRepo.getCardMemberRelationship(
|
||||||
.from("_card_workspace_members")
|
ctx.db,
|
||||||
.select()
|
cardMemberIds,
|
||||||
.eq("cardId", card.data.id)
|
);
|
||||||
.eq("workspaceMemberId", member.data.id)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (existingMember.data) {
|
if (existingMember) {
|
||||||
await ctx.db
|
await cardRepo.destroyCardMemberRelationship(ctx.db, cardMemberIds);
|
||||||
.from("_card_workspace_members")
|
|
||||||
.delete()
|
|
||||||
.eq("cardId", card.data.id)
|
|
||||||
.eq("workspaceMemberId", member.data.id);
|
|
||||||
|
|
||||||
return { newMember: false };
|
return { newMember: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.db.from("_card_workspace_members").insert({
|
await cardRepo.createCardMemberRelationship(ctx.db, cardMemberIds);
|
||||||
cardId: card.data.id,
|
|
||||||
workspaceMemberId: member.data.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { newMember: true };
|
return { newMember: true };
|
||||||
}),
|
}),
|
||||||
byId: protectedProcedure
|
byId: protectedProcedure
|
||||||
.input(z.object({ id: z.string().min(12) }))
|
.input(z.object({ id: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const result = await cardRepo.getWithListAndMembersByPublicId(
|
||||||
.from("card")
|
ctx.db,
|
||||||
.select(
|
input.id,
|
||||||
`
|
);
|
||||||
publicId,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
colourCode
|
|
||||||
),
|
|
||||||
list (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
board (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
|
||||||
colourCode,
|
|
||||||
name
|
|
||||||
),
|
|
||||||
lists:list (
|
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
),
|
|
||||||
workspace (
|
|
||||||
publicId,
|
|
||||||
members:workspace_members (
|
|
||||||
publicId,
|
|
||||||
user (
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
members:workspace_members (
|
|
||||||
publicId,
|
|
||||||
user (
|
|
||||||
id,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.eq("publicId", input.id)
|
|
||||||
.is("deletedAt", null)
|
|
||||||
.is("list.board.lists.deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -257,13 +171,13 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const result = cardRepo.update(
|
||||||
.from("card")
|
ctx.db,
|
||||||
.update({ title: input.title, description: input.description })
|
{ title: input.title, description: input.description },
|
||||||
.eq("publicId", input.cardId)
|
{ cardPublicId: input.cardId },
|
||||||
.is("deletedAt", null);
|
);
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
delete: protectedProcedure
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -276,30 +190,24 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await cardRepo.getCardWithListByPublicId(
|
||||||
.from("card")
|
ctx.db,
|
||||||
.select(`id, index, listId`)
|
input.cardPublicId,
|
||||||
.eq("publicId", input.cardPublicId)
|
);
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data) return;
|
if (!card || !card?.list?.id) return;
|
||||||
|
|
||||||
const deletedAt = new Date().toISOString();
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
await ctx.db
|
await cardRepo.destroy(ctx.db, {
|
||||||
.from("card")
|
cardId: card.id,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
deletedAt,
|
||||||
.eq("publicId", input.cardPublicId);
|
deletedBy: userId,
|
||||||
|
});
|
||||||
|
|
||||||
await ctx.db
|
await cardRepo.shiftIndex(ctx.db, {
|
||||||
.from("card")
|
listId: card.list.id,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
cardIndex: card.index,
|
||||||
.eq("publicId", input.cardPublicId);
|
|
||||||
|
|
||||||
await ctx.db.rpc("shift_card_index", {
|
|
||||||
list_id: card.data.listId,
|
|
||||||
card_index: card.data.index,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
reorder: protectedProcedure
|
reorder: protectedProcedure
|
||||||
@@ -315,50 +223,43 @@ export const cardRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await cardRepo.getCardWithListByPublicId(
|
||||||
.from("card")
|
ctx.db,
|
||||||
.select(`id, index, list (id)`)
|
input.cardId,
|
||||||
.eq("publicId", input.cardId)
|
);
|
||||||
.is("deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data) return;
|
if (!card) return;
|
||||||
|
|
||||||
const currentList = card.data.list;
|
const currentList = card.list;
|
||||||
const currentIndex = card.data.index;
|
const currentIndex = card.index;
|
||||||
|
|
||||||
let newIndex = input.newIndex;
|
let newIndex = input.newIndex;
|
||||||
|
|
||||||
const newList = await ctx.db
|
const newList = await listRepo.getWithCardsByPublicId(
|
||||||
.from("list")
|
ctx.db,
|
||||||
.select(`id, cards:card (index)`)
|
input.newListId,
|
||||||
.eq("publicId", input.newListId)
|
);
|
||||||
.is("deletedAt", null)
|
|
||||||
.order("index", { foreignTable: "card", ascending: false })
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!newList.data) return;
|
if (!newList) return;
|
||||||
|
|
||||||
if (newIndex === undefined) {
|
if (newIndex === undefined) {
|
||||||
const lastCardIndex = newList.data.cards.length
|
const lastCardIndex = newList.cards.length
|
||||||
? newList.data.cards[0]?.index
|
? newList.cards[0]?.index
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0;
|
newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentList?.id || !newList?.data.id) return;
|
if (!currentList?.id || !newList.id) return;
|
||||||
|
|
||||||
const { error } = await ctx.db.rpc("reorder_cards", {
|
const result = await cardRepo.reorder(ctx.db, {
|
||||||
current_list_id: currentList.id,
|
currentListId: currentList.id,
|
||||||
new_list_id: newList.data.id,
|
newListId: newList.id,
|
||||||
current_index: currentIndex,
|
currentIndex,
|
||||||
new_index: newIndex,
|
newIndex,
|
||||||
card_id: card.data.id,
|
cardId: card.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { success: !!error };
|
return { success: !!result.error };
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
|
import * as boardRepo from "~/server/db/repository/board.repo";
|
||||||
|
import * as cardRepo from "~/server/db/repository/card.repo";
|
||||||
|
import * as importRepo from "~/server/db/repository/import.repo";
|
||||||
|
import * as listRepo from "~/server/db/repository/list.repo";
|
||||||
|
import * as workspaceRepo from "~/server/db/repository/workspace.repo";
|
||||||
|
|
||||||
const TRELLO_API_URL = "https://api.trello.com/1";
|
const TRELLO_API_URL = "https://api.trello.com/1";
|
||||||
|
|
||||||
interface TrelloBoard {
|
interface TrelloBoard {
|
||||||
@@ -87,31 +91,21 @@ export const importRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const newImport = await ctx.db
|
const newImport = await importRepo.create(ctx.db, {
|
||||||
.from("import")
|
source: "trello",
|
||||||
.insert({
|
createdBy: userId,
|
||||||
publicId: generateUID(),
|
});
|
||||||
source: "trello",
|
|
||||||
createdBy: userId,
|
|
||||||
status: "started",
|
|
||||||
})
|
|
||||||
.select(`id`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const newImportId = newImport.data?.id;
|
const newImportId = newImport?.id;
|
||||||
|
|
||||||
let boardsCreated = 0;
|
let boardsCreated = 0;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const workspace = await workspaceRepo.getByPublicId(
|
||||||
.from("workspace")
|
ctx.db,
|
||||||
.select(`id`)
|
input.workspacePublicId,
|
||||||
.eq("publicId", input.workspacePublicId)
|
);
|
||||||
.is("deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace) return;
|
||||||
|
|
||||||
for (const boardId of input.boardIds) {
|
for (const boardId of input.boardIds) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@@ -132,41 +126,29 @@ export const importRouter = createTRPCRouter({
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const newBoard = await ctx.db
|
const newBoard = await boardRepo.create(ctx.db, {
|
||||||
.from("board")
|
name: formattedData.name,
|
||||||
.insert({
|
createdBy: userId,
|
||||||
publicId: generateUID(),
|
importId: newImportId,
|
||||||
name: data.name,
|
workspaceId: workspace.id,
|
||||||
createdBy: userId,
|
});
|
||||||
importId: newImportId,
|
|
||||||
workspaceId: workspace.data.id,
|
|
||||||
})
|
|
||||||
.select(`id`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const newBoardId = newBoard.data?.id;
|
const newBoardId = newBoard?.id;
|
||||||
|
|
||||||
if (!newBoardId) return;
|
if (!newBoardId) return;
|
||||||
|
|
||||||
let listIndex = 0;
|
let listIndex = 0;
|
||||||
|
|
||||||
for (const list of formattedData.lists) {
|
for (const list of formattedData.lists) {
|
||||||
const newList = await ctx.db
|
const newList = await listRepo.create(ctx.db, {
|
||||||
.from("list")
|
name: list.name,
|
||||||
.insert({
|
createdBy: userId,
|
||||||
publicId: generateUID(),
|
boardId: newBoardId,
|
||||||
name: list.name,
|
index: listIndex,
|
||||||
createdBy: userId,
|
importId: newImportId,
|
||||||
boardId: newBoardId,
|
});
|
||||||
index: listIndex,
|
|
||||||
importId: newImportId,
|
|
||||||
})
|
|
||||||
.select(`id`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const newListId = newList.data?.id;
|
const newListId = newList?.id;
|
||||||
|
|
||||||
if (list.cards.length && newListId) {
|
if (list.cards.length && newListId) {
|
||||||
const cardsInsert = list.cards.map((card, index) => ({
|
const cardsInsert = list.cards.map((card, index) => ({
|
||||||
@@ -179,6 +161,8 @@ export const importRouter = createTRPCRouter({
|
|||||||
importId: newImportId,
|
importId: newImportId,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
await cardRepo.bulkCreate(ctx.db, cardsInsert);
|
||||||
|
|
||||||
await ctx.db.from("card").insert(cardsInsert);
|
await ctx.db.from("card").insert(cardsInsert);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,10 +172,11 @@ export const importRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (boardsCreated > 0 && newImportId) {
|
if (boardsCreated > 0 && newImportId) {
|
||||||
await ctx.db
|
await importRepo.update(
|
||||||
.from("import")
|
ctx.db,
|
||||||
.update({ status: "success" })
|
{ status: "success" },
|
||||||
.eq("importId", newImportId);
|
{ importId: newImport.id },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { boardsCreated };
|
return { boardsCreated };
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
import * as cardRepo from "~/server/db/repository/card.repo";
|
||||||
|
import * as labelRepo from "~/server/db/repository/label.repo";
|
||||||
|
|
||||||
export const labelRouter = createTRPCRouter({
|
export const labelRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -17,36 +19,20 @@ export const labelRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await cardRepo.getCardWithListByPublicId(
|
||||||
.from("card")
|
ctx.db,
|
||||||
.select(`id, list (boardId)`)
|
input.cardPublicId,
|
||||||
.eq("publicId", input.cardPublicId)
|
);
|
||||||
.is("deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data?.list) return;
|
if (!card?.list) return;
|
||||||
|
|
||||||
const newLabel = await ctx.db
|
const result = await labelRepo.create(ctx.db, {
|
||||||
.from("label")
|
name: input.name,
|
||||||
.insert({
|
colourCode: input.colourCode,
|
||||||
publicId: generateUID(),
|
createdBy: userId,
|
||||||
name: input.name,
|
boardId: card.list.boardId,
|
||||||
colourCode: input.colourCode,
|
|
||||||
createdBy: userId,
|
|
||||||
boardId: card.data.list.boardId,
|
|
||||||
})
|
|
||||||
.select(`id`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!newLabel.data) return;
|
|
||||||
|
|
||||||
await ctx.db.from("_card_labels").insert({
|
|
||||||
cardId: card.data.id,
|
|
||||||
labelId: newLabel.data.id,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return newLabel.data;
|
return result;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
import * as cardRepo from "~/server/db/repository/card.repo";
|
||||||
|
import * as boardRepo from "~/server/db/repository/board.repo";
|
||||||
|
import * as listRepo from "~/server/db/repository/list.repo";
|
||||||
|
|
||||||
export const listRouter = createTRPCRouter({
|
export const listRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -16,31 +19,23 @@ export const listRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const board = await ctx.db
|
const board = await boardRepo.getWithLatestListIndexByPublicId(
|
||||||
.from("board")
|
ctx.db,
|
||||||
.select(`id, lists:list (index)`)
|
input.boardPublicId,
|
||||||
.eq("publicId", input.boardPublicId)
|
);
|
||||||
.order("index", { foreignTable: "list", ascending: false })
|
|
||||||
.is("list.deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!board.data) return;
|
if (!board) return;
|
||||||
|
|
||||||
const latestListIndex = board.data.lists[0]?.index;
|
const latestListIndex = board.lists[0]?.index;
|
||||||
|
|
||||||
const { data } = await ctx.db.from("list").insert({
|
const result = await listRepo.create(ctx.db, {
|
||||||
publicId: generateUID(),
|
|
||||||
name: input.name,
|
name: input.name,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
boardId: board.data.id,
|
boardId: board.id,
|
||||||
index: latestListIndex ? latestListIndex + 1 : 0,
|
index: latestListIndex ? latestListIndex + 1 : 0,
|
||||||
}).select(`
|
});
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
`);
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
reorder: protectedProcedure
|
reorder: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -52,23 +47,18 @@ export const listRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const list = await ctx.db
|
const list = await listRepo.getByPublicId(ctx.db, input.listId);
|
||||||
.from("list")
|
|
||||||
.select(`id, boardId`)
|
|
||||||
.eq("publicId", input.listId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!list?.data) return;
|
if (!list) return;
|
||||||
|
|
||||||
const { data } = await ctx.db.rpc("reorder_lists", {
|
const result = listRepo.reorder(ctx.db, {
|
||||||
board_id: list.data.boardId,
|
boardPublicId: list.boardId,
|
||||||
list_id: list.data.id,
|
listPublicId: list.id,
|
||||||
current_index: input.currentIndex,
|
currentIndex: input.currentIndex,
|
||||||
new_index: input.newIndex,
|
newIndex: input.newIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
delete: protectedProcedure
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -79,35 +69,28 @@ export const listRouter = createTRPCRouter({
|
|||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
const list = await ctx.db
|
const list = await listRepo.getByPublicId(ctx.db, input.listPublicId);
|
||||||
.from("list")
|
|
||||||
.select(`id, boardId, index`)
|
|
||||||
.eq("publicId", input.listPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!list.data) return;
|
if (!list || !userId) return;
|
||||||
|
|
||||||
const deletedAt = new Date().toISOString();
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
await ctx.db
|
await listRepo.destroyById(ctx.db, {
|
||||||
.from("list")
|
listId: list.id,
|
||||||
.update({ deletedAt, deletedBy: userId })
|
deletedAt,
|
||||||
.eq("id", list.data.id)
|
deletedBy: userId,
|
||||||
.is("deletedAt", null);
|
|
||||||
|
|
||||||
await ctx.db
|
|
||||||
.from("card")
|
|
||||||
.update({ deletedAt, deletedBy: userId })
|
|
||||||
.eq("listId", list.data.id)
|
|
||||||
.is("deletedAt", null);
|
|
||||||
|
|
||||||
const { data } = await ctx.db.rpc("shift_list_index", {
|
|
||||||
board_id: list.data.boardId,
|
|
||||||
list_index: list.data.index,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
await cardRepo.destroyAllByListIds(ctx.db, {
|
||||||
|
listIds: [list.id],
|
||||||
|
deletedAt,
|
||||||
|
deletedBy: userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
await listRepo.shiftIndex(ctx.db, {
|
||||||
|
boardId: list.boardId,
|
||||||
|
listIndex: list.id,
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -117,13 +100,10 @@ export const listRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const result = await listRepo.update(
|
||||||
.from("list")
|
ctx.db,
|
||||||
.update({ name: input.name })
|
{ name: input.name },
|
||||||
.eq("publicId", input.listPublicId)
|
{ listPublicId: input.listPublicId },
|
||||||
.is("deletedAt", null)
|
);
|
||||||
.select(`publicId, name`);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
|
import * as workspaceRepo from "~/server/db/repository/workspace.repo";
|
||||||
|
|
||||||
export const workspaceRouter = createTRPCRouter({
|
export const workspaceRouter = createTRPCRouter({
|
||||||
all: protectedProcedure.query(async ({ ctx }) => {
|
all: protectedProcedure.query(async ({ ctx }) => {
|
||||||
@@ -9,47 +10,19 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const result = await workspaceRepo.getAllByUserId(ctx.db, userId);
|
||||||
.from("workspace_members")
|
|
||||||
.select(
|
|
||||||
`
|
|
||||||
role,
|
|
||||||
workspace (
|
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.eq("userId", userId)
|
|
||||||
.is("deletedAt", null);
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
byId: protectedProcedure
|
byId: protectedProcedure
|
||||||
.input(z.object({ publicId: z.string().min(12) }))
|
.input(z.object({ publicId: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const result = await workspaceRepo.getByPublicIdWithMembers(
|
||||||
.from("workspace")
|
ctx.db,
|
||||||
.select(
|
input.publicId,
|
||||||
`
|
);
|
||||||
publicId,
|
|
||||||
members: workspace_members (
|
|
||||||
publicId,
|
|
||||||
role,
|
|
||||||
user (
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
email
|
|
||||||
)
|
|
||||||
)
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.eq("publicId", input.publicId)
|
|
||||||
.is("deletedAt", null)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}),
|
}),
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -62,34 +35,12 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const result = await workspaceRepo.create(ctx.db, {
|
||||||
.from("workspace")
|
name: input.name,
|
||||||
.insert({
|
slug: input.name,
|
||||||
publicId: generateUID(),
|
|
||||||
name: input.name,
|
|
||||||
slug: input.name.toLowerCase(),
|
|
||||||
createdBy: userId,
|
|
||||||
})
|
|
||||||
.select(`id, publicId, name`)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const newWorkspaceId = workspace.data?.id;
|
|
||||||
|
|
||||||
if (!newWorkspaceId) return;
|
|
||||||
|
|
||||||
await ctx.db.from("workspace_members").insert({
|
|
||||||
publicId: generateUID(),
|
|
||||||
userId,
|
|
||||||
workspaceId: newWorkspaceId,
|
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
role: "admin",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const newWorkspace = { ...workspace.data };
|
return result;
|
||||||
|
|
||||||
delete newWorkspace.id;
|
|
||||||
|
|
||||||
return newWorkspace;
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
162
src/server/db/repository/board.repo.ts
Normal file
162
src/server/db/repository/board.repo.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const getAllByWorkspaceId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
workspaceId: number,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select(`publicId, name`)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.eq("workspaceId", workspaceId);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
workspace (
|
||||||
|
publicId,
|
||||||
|
members:workspace_members (
|
||||||
|
publicId,
|
||||||
|
user (
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
colourCode
|
||||||
|
),
|
||||||
|
lists:list (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
boardId,
|
||||||
|
index,
|
||||||
|
cards:card (
|
||||||
|
publicId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
listId,
|
||||||
|
index,
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
colourCode
|
||||||
|
),
|
||||||
|
members:workspace_members (
|
||||||
|
publicId,
|
||||||
|
user (
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.eq("publicId", boardPublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.is("lists.deletedAt", null)
|
||||||
|
.is("lists.cards.deletedAt", null)
|
||||||
|
.order("index", { foreignTable: "list", ascending: true })
|
||||||
|
.order("index", { foreignTable: "list.card", ascending: true })
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWithListIdsByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select(`id, lists:list (id)`)
|
||||||
|
.eq("publicId", boardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWithLatestListIndexByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.select(`id, lists:list (index)`)
|
||||||
|
.eq("publicId", boardPublicId)
|
||||||
|
.order("index", { foreignTable: "list", ascending: false })
|
||||||
|
.is("list.deletedAt", null)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardInput: {
|
||||||
|
name: string;
|
||||||
|
createdBy: string;
|
||||||
|
workspaceId: number;
|
||||||
|
importId?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
name: boardInput.name,
|
||||||
|
createdBy: boardInput.createdBy,
|
||||||
|
workspaceId: boardInput.workspaceId,
|
||||||
|
importId: boardInput.importId,
|
||||||
|
})
|
||||||
|
.select(`id, publicId, name`)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
boardInput: { name: string; boardPublicId: string },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("board")
|
||||||
|
.update({ name: boardInput.name })
|
||||||
|
.eq("publicId", boardInput.boardPublicId);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroy = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
boardId: number;
|
||||||
|
deletedAt: string;
|
||||||
|
deletedBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = db
|
||||||
|
.from("board")
|
||||||
|
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
|
.eq("id", args.boardId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
329
src/server/db/repository/card.repo.ts
Normal file
329
src/server/db/repository/card.repo.ts
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardInput: {
|
||||||
|
title: string;
|
||||||
|
createdBy: string;
|
||||||
|
listId: number;
|
||||||
|
index: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("card")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
title: cardInput.title,
|
||||||
|
createdBy: cardInput.createdBy,
|
||||||
|
listId: cardInput.listId,
|
||||||
|
index: cardInput.index,
|
||||||
|
})
|
||||||
|
.select(`id`)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bulkCreateCardLabelRelationships = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardLabelRelationshipInput: {
|
||||||
|
cardId: number;
|
||||||
|
labelId: number;
|
||||||
|
}[],
|
||||||
|
) => {
|
||||||
|
const result = db.from("_card_labels").insert(cardLabelRelationshipInput);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bulkCreateCardWorkspaceMemberRelationships = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardWorkspaceMemberRelationshipInput: {
|
||||||
|
cardId: number;
|
||||||
|
workspaceMemberId: number;
|
||||||
|
}[],
|
||||||
|
) => {
|
||||||
|
const result = db
|
||||||
|
.from("_card_workspace_members")
|
||||||
|
.insert(cardWorkspaceMemberRelationshipInput);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardInput: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
cardPublicId: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("card")
|
||||||
|
.update({ title: cardInput.title, description: cardInput.description })
|
||||||
|
.eq("publicId", args.cardPublicId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroy = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
cardId: number;
|
||||||
|
deletedAt: string;
|
||||||
|
deletedBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("card")
|
||||||
|
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
|
.eq("publicId", args.cardId);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroyAllByListIds = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
listIds: number[];
|
||||||
|
deletedAt: string;
|
||||||
|
deletedBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("card")
|
||||||
|
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
|
.in("listId", args.listIds)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCardWithListByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("card")
|
||||||
|
.select(`id, index, list (id, boardId)`)
|
||||||
|
.eq("publicId", cardPublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("card")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", cardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCardLabelRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: { cardId: number; labelId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("_card_labels")
|
||||||
|
.select()
|
||||||
|
.eq("cardId", args.cardId)
|
||||||
|
.eq("labelId", args.labelId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bulkCreate = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardInput: {
|
||||||
|
publicId: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
createdBy: string;
|
||||||
|
listId: number;
|
||||||
|
index: number;
|
||||||
|
importId?: number;
|
||||||
|
}[],
|
||||||
|
) => {
|
||||||
|
const data = await db.from("card").insert(cardInput);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroyCardLabelRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: { cardId: number; labelId: number },
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("_card_labels")
|
||||||
|
.delete()
|
||||||
|
.eq("cardId", args.cardId)
|
||||||
|
.eq("labelId", args.labelId);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createCardLabelRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardLabelRelationshipInput: { cardId: number; labelId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db.from("_card_labels").insert({
|
||||||
|
cardId: cardLabelRelationshipInput.cardId,
|
||||||
|
labelId: cardLabelRelationshipInput.labelId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCardMemberRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: { cardId: number; memberId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("_card_workspace_members")
|
||||||
|
.select()
|
||||||
|
.eq("cardId", args.cardId)
|
||||||
|
.eq("workspaceMemberId", args.memberId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroyCardMemberRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: { cardId: number; memberId: number },
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("_card_workspace_members")
|
||||||
|
.delete()
|
||||||
|
.eq("cardId", args.cardId)
|
||||||
|
.eq("workspaceMemberId", args.memberId);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createCardMemberRelationship = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardMemberRelationshipInput: { cardId: number; memberId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db.from("_card_workspace_members").insert({
|
||||||
|
cardId: cardMemberRelationshipInput.cardId,
|
||||||
|
workspaceMemberId: cardMemberRelationshipInput.memberId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWithListAndMembersByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
cardPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("card")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
publicId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
colourCode
|
||||||
|
),
|
||||||
|
list (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
board (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
colourCode,
|
||||||
|
name
|
||||||
|
),
|
||||||
|
lists:list (
|
||||||
|
publicId,
|
||||||
|
name
|
||||||
|
),
|
||||||
|
workspace (
|
||||||
|
publicId,
|
||||||
|
members:workspace_members (
|
||||||
|
publicId,
|
||||||
|
user (
|
||||||
|
id,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
members:workspace_members (
|
||||||
|
publicId,
|
||||||
|
user (
|
||||||
|
id,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.eq("publicId", cardPublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.is("list.board.lists.deletedAt", null)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reorder = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
currentListId: number;
|
||||||
|
newListId: number;
|
||||||
|
currentIndex: number;
|
||||||
|
newIndex: number;
|
||||||
|
cardId: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = await db.rpc("reorder_cards", {
|
||||||
|
current_list_id: args.currentListId,
|
||||||
|
new_list_id: args.newListId,
|
||||||
|
current_index: args.currentIndex,
|
||||||
|
new_index: args.newIndex,
|
||||||
|
card_id: args.cardId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shiftIndex = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
listId: number;
|
||||||
|
cardIndex: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db.rpc("shift_card_index", {
|
||||||
|
list_id: args.listId,
|
||||||
|
card_index: args.cardIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
37
src/server/db/repository/import.repo.ts
Normal file
37
src/server/db/repository/import.repo.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
importInput: { source: string; createdBy: string },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("import")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
source: "trello",
|
||||||
|
createdBy: importInput.createdBy,
|
||||||
|
status: "started",
|
||||||
|
})
|
||||||
|
.select(`id`)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
importInput: { status: "started" | "success" | "failed" },
|
||||||
|
args: { importId: number },
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("import")
|
||||||
|
.update({ status: importInput.status })
|
||||||
|
.eq("importId", args.importId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
61
src/server/db/repository/label.repo.ts
Normal file
61
src/server/db/repository/label.repo.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
labelInput: {
|
||||||
|
name: string;
|
||||||
|
colourCode: string;
|
||||||
|
createdBy: string;
|
||||||
|
boardId: number;
|
||||||
|
cardId?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("label")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
name: labelInput.name,
|
||||||
|
colourCode: labelInput.colourCode,
|
||||||
|
createdBy: labelInput.createdBy,
|
||||||
|
boardId: labelInput.boardId,
|
||||||
|
})
|
||||||
|
.select(`id`)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (labelInput.cardId && data)
|
||||||
|
await db.from("_card_labels").insert({
|
||||||
|
cardId: labelInput.cardId,
|
||||||
|
labelId: data.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllByPublicIds = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
labelPublicIds: string[],
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("label")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", labelPublicIds);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
labelPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("label")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", labelPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
154
src/server/db/repository/list.repo.ts
Normal file
154
src/server/db/repository/list.repo.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
listInput: {
|
||||||
|
name: string;
|
||||||
|
createdBy: string;
|
||||||
|
boardId: number;
|
||||||
|
index: number;
|
||||||
|
importId?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("list")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
name: listInput.name,
|
||||||
|
createdBy: listInput.createdBy,
|
||||||
|
boardId: listInput.boardId,
|
||||||
|
index: listInput.index,
|
||||||
|
importId: listInput.importId,
|
||||||
|
})
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
id,
|
||||||
|
publicId,
|
||||||
|
name
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
listPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("list")
|
||||||
|
.select(`id, boardId, index`)
|
||||||
|
.eq("publicId", listPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getWithCardsByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
listPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("list")
|
||||||
|
.select(`id, cards:card (index)`)
|
||||||
|
.eq("publicId", listPublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.is("card.deletedAt", null)
|
||||||
|
.order("index", { foreignTable: "card", ascending: false })
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
listInput: {
|
||||||
|
name: string;
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
listPublicId: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("list")
|
||||||
|
.update({ name: listInput.name })
|
||||||
|
.eq("publicId", args.listPublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.select(`publicId, name`);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroyAllByBoardId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
boardId: number;
|
||||||
|
deletedAt: string;
|
||||||
|
deletedBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("list")
|
||||||
|
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
|
.eq("boardId", args.boardId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const destroyById = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
listId: number;
|
||||||
|
deletedAt: string;
|
||||||
|
deletedBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const result = await db
|
||||||
|
.from("list")
|
||||||
|
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
|
.eq("id", args.listId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reorder = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
boardPublicId: number;
|
||||||
|
listPublicId: number;
|
||||||
|
currentIndex: number;
|
||||||
|
newIndex: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db.rpc("reorder_lists", {
|
||||||
|
board_id: args.boardPublicId,
|
||||||
|
list_id: args.listPublicId,
|
||||||
|
current_index: args.currentIndex,
|
||||||
|
new_index: args.newIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shiftIndex = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
args: {
|
||||||
|
boardId: number;
|
||||||
|
listIndex: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db.rpc("shift_list_index", {
|
||||||
|
board_id: args.boardId,
|
||||||
|
list_index: args.listIndex,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
13
src/server/db/repository/user.repo.ts
Normal file
13
src/server/db/repository/user.repo.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const getById = async (db: SupabaseClient<Database>, userId: string) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("user")
|
||||||
|
.select(`id, name, email`)
|
||||||
|
.eq("id", userId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
129
src/server/db/repository/workspace.repo.ts
Normal file
129
src/server/db/repository/workspace.repo.ts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
|
export const create = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
workspaceInput: {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
createdBy: string;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace")
|
||||||
|
.insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
name: workspaceInput.name,
|
||||||
|
slug: workspaceInput.name.toLowerCase(),
|
||||||
|
createdBy: workspaceInput.createdBy,
|
||||||
|
})
|
||||||
|
.select(`id, publicId, name`)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
await db.from("workspace_members").insert({
|
||||||
|
publicId: generateUID(),
|
||||||
|
userId: workspaceInput.createdBy,
|
||||||
|
workspaceId: data.id,
|
||||||
|
createdBy: workspaceInput.createdBy,
|
||||||
|
role: "admin",
|
||||||
|
});
|
||||||
|
|
||||||
|
const newWorkspace = { ...data };
|
||||||
|
|
||||||
|
delete newWorkspace.id;
|
||||||
|
|
||||||
|
return newWorkspace;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
workspacePublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace")
|
||||||
|
.select(`id, publicId, name`)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.eq("publicId", workspacePublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getByPublicIdWithMembers = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
workspacePublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
publicId,
|
||||||
|
members: workspace_members (
|
||||||
|
publicId,
|
||||||
|
role,
|
||||||
|
user (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
email
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.eq("publicId", workspacePublicId)
|
||||||
|
.is("deletedAt", null)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllByUserId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
userId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace_members")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
|
role,
|
||||||
|
workspace (
|
||||||
|
publicId,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.eq("userId", userId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMemberByPublicId = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
memberPublicId: string,
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace_members")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", memberPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAllMembersByPublicIds = async (
|
||||||
|
db: SupabaseClient<Database>,
|
||||||
|
memberPublicIds: string[],
|
||||||
|
) => {
|
||||||
|
const { data } = await db
|
||||||
|
.from("workspace_members")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", memberPublicIds);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user