From 4e1e99cbac0c2f55e672d93fe64eb36b5f56cb80 Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 17 Apr 2024 18:18:33 +0100 Subject: [PATCH] feat: finish swapping drizzle for supabase --- src/server/api/routers/auth.ts | 2 +- src/server/api/routers/board.ts | 20 ++-- src/server/api/routers/card.ts | 173 ++++++++++++++-------------- src/server/api/routers/import.ts | 12 +- src/server/api/routers/label.ts | 6 +- src/server/api/routers/list.ts | 18 +-- src/server/api/routers/workspace.ts | 8 +- src/server/api/trpc.ts | 15 +-- src/server/auth.ts | 86 -------------- src/server/db/schema.ts | 58 ---------- src/types/database.types.ts | 19 ++- supabase/seed.sql | 49 +++++++- 12 files changed, 189 insertions(+), 277 deletions(-) delete mode 100644 src/server/auth.ts diff --git a/src/server/api/routers/auth.ts b/src/server/api/routers/auth.ts index 1d1320f7..04c932fa 100644 --- a/src/server/api/routers/auth.ts +++ b/src/server/api/routers/auth.ts @@ -9,7 +9,7 @@ export const authRouter = createTRPCRouter({ login: publicProcedure .input(z.object({ email: z.string() })) .mutation(async ({ ctx, input }) => { - const { data } = await ctx.supabase.auth.signInWithOtp({ + const { data } = await ctx.db.auth.signInWithOtp({ email: input.email, options: { emailRedirectTo: '/boards', diff --git a/src/server/api/routers/board.ts b/src/server/api/routers/board.ts index 2b8c6377..52e13974 100644 --- a/src/server/api/routers/board.ts +++ b/src/server/api/routers/board.ts @@ -10,7 +10,7 @@ export const boardRouter = createTRPCRouter({ all: publicProcedure .input(z.object({ workspacePublicId: z.string().min(12) })) .query(async ({ ctx, input }) => { - const workspace = await ctx.supabase + const workspace = await ctx.db .from('workspace') .select(`id`) .eq('publicId', input.workspacePublicId) @@ -19,7 +19,7 @@ export const boardRouter = createTRPCRouter({ if (!workspace.data) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('board') .select(` publicId, @@ -33,7 +33,7 @@ export const boardRouter = createTRPCRouter({ byId: publicProcedure .input(z.object({ id: z.string().min(12) })) .query(async ({ ctx, input }) => { - const { data } = await ctx.supabase + const { data } = await ctx.db .from('board') .select(` publicId, @@ -100,7 +100,7 @@ export const boardRouter = createTRPCRouter({ if (!userId) return; - const workspace = await ctx.supabase + const workspace = await ctx.db .from('workspace') .select(`id`) .eq('publicId', input.workspacePublicId) @@ -109,7 +109,7 @@ export const boardRouter = createTRPCRouter({ if (!workspace.data) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('board') .insert({ publicId: generateUID(), @@ -135,7 +135,7 @@ export const boardRouter = createTRPCRouter({ if (!userId) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('board') .update({ name: input.name }) .eq('publicId', input.boardId); @@ -152,7 +152,7 @@ export const boardRouter = createTRPCRouter({ if (!userId) return; - const board = await ctx.supabase + const board = await ctx.db .from('board') .select(` id, @@ -168,20 +168,20 @@ export const boardRouter = createTRPCRouter({ const deletedAt = new Date().toISOString(); - await ctx.supabase + await ctx.db .from('board') .update({ deletedAt, deletedBy: userId }) .eq('id', board.data.id) .is('deletedAt', null); if (listIds.length) { - await ctx.supabase + await ctx.db .from('list') .update({ deletedAt, deletedBy: userId }) .eq('boardId', board.data.id) .is('deletedAt', null); - await ctx.supabase + await ctx.db .from('card') .update({ deletedAt, deletedBy: userId }) .in('listId', listIds) diff --git a/src/server/api/routers/card.ts b/src/server/api/routers/card.ts index c450bbcf..ba70c735 100644 --- a/src/server/api/routers/card.ts +++ b/src/server/api/routers/card.ts @@ -1,7 +1,4 @@ import { z } from "zod"; -import { and, desc, eq, isNull, inArray, sql } from "drizzle-orm"; - -import { cards, cardsToLabels, cardToWorkspaceMembers, labels, lists, workspaceMembers } from "~/server/db/schema"; import { generateUID } from "~/utils/generateUID"; import { @@ -24,7 +21,7 @@ export const cardRouter = createTRPCRouter({ if (!userId) return; - const list = await ctx.supabase + const list = await ctx.db .from('list') .select(`id, cards:card (index)`) .eq('publicId', input.listPublicId) @@ -36,7 +33,7 @@ export const cardRouter = createTRPCRouter({ const latestCard = list.data.cards.length && list.data.cards[0] - const newCard = await ctx.supabase + const newCard = await ctx.db .from('card') .insert({ publicId: generateUID(), @@ -52,7 +49,7 @@ export const cardRouter = createTRPCRouter({ const newCardId = newCard.data?.id; if (newCardId && input.labelsPublicIds.length) { - const labels = await ctx.supabase + const labels = await ctx.db .from('label') .select(`id`) .eq('publicId', input.labelsPublicIds); @@ -61,13 +58,13 @@ export const cardRouter = createTRPCRouter({ const labelsInsert = labels.data.map((label) => ({ cardId: newCardId, labelId: label.id })); - await ctx.supabase + await ctx.db .from('_card_labels') .insert(labelsInsert); } if (newCardId && input.memberPublicIds.length) { - const members = await ctx.supabase + const members = await ctx.db .from('workspace_members') .select(`id`) .eq('publicId', input.memberPublicIds); @@ -76,7 +73,7 @@ export const cardRouter = createTRPCRouter({ const membersInsert = members.data.map((member) => ({ cardId: newCardId, workspaceMemberId: member.id })); - await ctx.supabase + await ctx.db .from('_card_workspace_members') .insert(membersInsert); } @@ -95,14 +92,14 @@ export const cardRouter = createTRPCRouter({ if (!userId) return; - const card = await ctx.supabase + const card = await ctx.db .from('card') .select(`id`) .eq('publicId', input.cardPublicId) .limit(1) .single(); - const label = await ctx.supabase + const label = await ctx.db .from('label') .select(`id`) .eq('publicId', input.labelPublicId) @@ -111,7 +108,7 @@ export const cardRouter = createTRPCRouter({ if (!card.data || !label.data) return; - const existingLabel = await ctx.supabase + const existingLabel = await ctx.db .from('_card_labels') .select() .eq('cardId', card.data.id) @@ -120,7 +117,7 @@ export const cardRouter = createTRPCRouter({ .single(); if (existingLabel.data) { - await ctx.supabase + await ctx.db .from('_card_labels') .delete() .eq('cardId', card.data.id) @@ -129,7 +126,7 @@ export const cardRouter = createTRPCRouter({ return { newLabel: false }; } - await ctx.supabase + await ctx.db .from('_card_labels') .insert({ cardId: card.data.id, @@ -150,14 +147,14 @@ export const cardRouter = createTRPCRouter({ if (!userId) return; - const card = await ctx.supabase + const card = await ctx.db .from('card') .select(`id`) .eq('publicId', input.cardPublicId) .limit(1) .single(); - const member = await ctx.supabase + const member = await ctx.db .from('workspace_members') .select(`id`) .eq('publicId', input.workspaceMemberPublicId) @@ -166,7 +163,7 @@ export const cardRouter = createTRPCRouter({ if (!card.data || !member.data) return; - const existingMember = await ctx.supabase + const existingMember = await ctx.db .from('_card_workspace_members') .select() .eq('cardId', card.data.id) @@ -175,7 +172,7 @@ export const cardRouter = createTRPCRouter({ .single(); if (existingMember.data) { - await ctx.supabase + await ctx.db .from('_card_workspace_members') .delete() .eq('cardId', card.data.id) @@ -184,7 +181,7 @@ export const cardRouter = createTRPCRouter({ return { newMember: false }; } - await ctx.supabase + await ctx.db .from('_card_workspace_members') .insert({ cardId: card.data.id, @@ -196,7 +193,7 @@ export const cardRouter = createTRPCRouter({ byId: publicProcedure .input(z.object({ id: z.string().min(12) })) .query(async ({ ctx, input }) => { - const { data } = await ctx.supabase + const { data } = await ctx.db .from('card') .select(` publicId, @@ -262,7 +259,7 @@ export const cardRouter = createTRPCRouter({ if (!userId) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('card') .update({ title: input.title, description: input.description }) .eq('publicId', input.cardId) @@ -275,22 +272,37 @@ export const cardRouter = createTRPCRouter({ z.object({ cardPublicId: z.string().min(12), })) - .mutation(({ ctx, input }) => { + .mutation(async ({ ctx, input }) => { const userId = ctx.user?.id; if (!userId) return; - return ctx.db.transaction(async (tx) => { - const card = await tx.query.cards.findFirst({ - where: eq(cards.publicId, input.cardPublicId), - }) + const card = await ctx.db + .from('card') + .select(`id, index, listId`) + .eq('publicId', input.cardPublicId) + .limit(1) + .single(); - if (!card) return; + if (!card.data) return; - await tx.update(cards).set({ deletedAt: new Date(), deletedBy: userId}).where(eq(cards.publicId, input.cardPublicId)); + const deletedAt = new Date().toISOString(); - await tx.execute(sql`UPDATE ${cards} SET ${cards.index} = ${cards.index} - 1 WHERE ${cards.listId} = ${card.listId} AND ${cards.index} > ${card.index} AND ${cards.deletedAt} IS NULL;`); - }) + await ctx.db + .from('card') + .update({ deletedAt, deletedBy: userId}) + .eq('publicId', input.cardPublicId); + + await ctx.db + .from('card') + .update({ deletedAt, deletedBy: userId}) + .eq('publicId', input.cardPublicId); + + await ctx.db + .rpc('shift_card_index', { + list_id: card.data.listId, + card_index: card.data.index, + }); }), reorder: publicProcedure .input( @@ -299,69 +311,54 @@ export const cardRouter = createTRPCRouter({ newListId: z.string().min(12), newIndex: z.number().optional(), })) - .mutation(({ ctx, input }) => { + .mutation(async ({ ctx, input }) => { const userId = ctx.user?.id; if (!userId) return; - return ctx.db.transaction(async (tx) => { - const card = await tx.query.cards.findFirst({ - with: { - list: true, - }, - where: and(eq(cards.publicId, input.cardId), isNull(cards.deletedAt)), + const card = await ctx.db + .from('card') + .select(`id, index, list (id)`) + .eq('publicId', input.cardId) + .is('deletedAt', null) + .limit(1) + .single(); + + if (!card.data) return; + + const currentList = card.data.list; + const currentIndex = card.data.index; + + let newIndex = input.newIndex; + + const newList = await ctx.db + .from('list') + .select(`id, cards:card (index)`) + .eq('publicId', input.newListId) + .is('deletedAt', null) + .order('index', { foreignTable: 'card', ascending: false }) + .limit(1) + .single(); + + if (!newList.data) return; + + if (newIndex === undefined) { + const lastCardIndex = newList.data.cards.length ? newList.data.cards[0]?.index : undefined; + + newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0; + } + + if (!currentList?.id || !newList?.data.id) return; + + const { error } = await ctx.db + .rpc('reorder_cards', { + current_list_id: currentList.id, + new_list_id: newList.data.id, + current_index: currentIndex, + new_index: newIndex, + card_id: card.data.id, }); - if (!card) return; - - const currentList = card.list; - const currentIndex = card.index; - - let newIndex = input.newIndex; - - const newList = await tx.query.lists.findFirst({ - with: { - cards: { - orderBy: [desc(cards.index)], - limit: 1, - }, - }, - where: and(eq(lists.publicId, input.newListId), isNull(cards.deletedAt)), - }); - - if (!newList) return; - - if (newIndex === undefined) { - const lastCardIndex = newList.cards.length ? newList.cards[0]?.index : undefined; - - newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0; - } - - if (!currentList?.id || !newList?.id) return; - - - if (currentList.id === newList.id) { - await tx.execute(sql` - UPDATE ${cards} - SET index = - CASE - WHEN ${cards.index} = ${currentIndex} THEN ${newIndex} - WHEN ${currentIndex} < ${newIndex} AND ${cards.index} > ${currentIndex} AND ${cards.index} <= ${newIndex} THEN ${cards.index} - 1 - WHEN ${currentIndex} > ${newIndex} AND ${cards.index} >= ${newIndex} AND ${cards.index} < ${currentIndex} THEN ${cards.index} + 1 - ELSE ${cards.index} - END - WHERE ${cards.listId} = ${currentList.id} AND ${cards.deletedAt} IS NULL; - `); - } else { - await tx.execute(sql`UPDATE ${cards} SET index = index + 1 WHERE ${cards.listId} = ${newList.id} AND ${cards.index} >= ${newIndex} AND ${cards.deletedAt} IS NULL;`) - - await tx.execute(sql`UPDATE ${cards} SET index = index - 1 WHERE ${cards.listId} = ${currentList.id} AND ${cards.index} >= ${currentIndex} AND ${cards.deletedAt} IS NULL;`) - - await tx - .update(cards) - .set({ listId: newList.id, index: newIndex }) - .where(and(eq(cards.publicId, input.cardId), isNull(cards.deletedAt))); - } - }) + return { success: !!error } }) }); \ No newline at end of file diff --git a/src/server/api/routers/import.ts b/src/server/api/routers/import.ts index 7b9f0739..53207bc3 100644 --- a/src/server/api/routers/import.ts +++ b/src/server/api/routers/import.ts @@ -87,7 +87,7 @@ export const importRouter = createTRPCRouter({ if (!userId) return; - const newImport = await ctx.supabase + const newImport = await ctx.db .from('import') .insert({ publicId: generateUID(), @@ -103,7 +103,7 @@ export const importRouter = createTRPCRouter({ let boardsCreated = 0; - const workspace = await ctx.supabase + const workspace = await ctx.db .from('workspace') .select(`id`) .eq('publicId', input.workspacePublicId) @@ -130,7 +130,7 @@ export const importRouter = createTRPCRouter({ })) } - const newBoard = await ctx.supabase + const newBoard = await ctx.db .from('board') .insert({ publicId: generateUID(), @@ -150,7 +150,7 @@ export const importRouter = createTRPCRouter({ let listIndex = 0; for (const list of formattedData.lists) { - const newList = await ctx.supabase + const newList = await ctx.db .from('list') .insert({ publicId: generateUID(), @@ -177,7 +177,7 @@ export const importRouter = createTRPCRouter({ importId: newImportId })) - await ctx.supabase + await ctx.db .from('card') .insert(cardsInsert); } @@ -188,7 +188,7 @@ export const importRouter = createTRPCRouter({ } if (boardsCreated > 0 && newImportId) { - await ctx.supabase + await ctx.db .from('import') .update({ status: 'success' }) .eq('importId', newImportId); diff --git a/src/server/api/routers/label.ts b/src/server/api/routers/label.ts index 597585a2..dde4f3f6 100644 --- a/src/server/api/routers/label.ts +++ b/src/server/api/routers/label.ts @@ -20,7 +20,7 @@ export const labelRouter = createTRPCRouter({ if (!userId) return; - const card = await ctx.supabase + const card = await ctx.db .from('card') .select(`id, list (boardId)`) .eq('publicId', input.cardPublicId) @@ -30,7 +30,7 @@ export const labelRouter = createTRPCRouter({ if (!card.data?.list) return; - const newLabel = await ctx.supabase + const newLabel = await ctx.db .from('label') .insert({ publicId: generateUID(), @@ -45,7 +45,7 @@ export const labelRouter = createTRPCRouter({ if (!newLabel.data) return; - await ctx.supabase + await ctx.db .from('_card_labels') .insert({ cardId: card.data.id, diff --git a/src/server/api/routers/list.ts b/src/server/api/routers/list.ts index b810d302..e9c67a63 100644 --- a/src/server/api/routers/list.ts +++ b/src/server/api/routers/list.ts @@ -19,7 +19,7 @@ export const listRouter = createTRPCRouter({ if (!userId) return; - const board = await ctx.supabase + const board = await ctx.db .from('board') .select(`id, lists:list (index)`) .eq('publicId', input.boardPublicId) @@ -32,7 +32,7 @@ export const listRouter = createTRPCRouter({ const latestListIndex = board.data.lists[0]?.index - const { data } = await ctx.supabase + const { data } = await ctx.db .from('list') .insert({ publicId: generateUID(), @@ -61,7 +61,7 @@ export const listRouter = createTRPCRouter({ if (!userId) return; - const list = await ctx.supabase + const list = await ctx.db .from('list') .select(`id, boardId`) .eq('publicId', input.listId) @@ -70,7 +70,7 @@ export const listRouter = createTRPCRouter({ if (!list?.data) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .rpc('reorder_list', { board_id: list.data.boardId, list_id: list.data.id, @@ -90,7 +90,7 @@ export const listRouter = createTRPCRouter({ if (!userId) return; - const list = await ctx.supabase + const list = await ctx.db .from('list') .select(`id, boardId, index`) .eq('publicId', input.listPublicId) @@ -101,19 +101,19 @@ export const listRouter = createTRPCRouter({ const deletedAt = new Date().toISOString(); - await ctx.supabase + await ctx.db .from('list') .update({ deletedAt, deletedBy: userId }) .eq('id', list.data.id) .is('deletedAt', null); - await ctx.supabase + await ctx.db .from('card') .update({ deletedAt, deletedBy: userId }) .eq('listId', list.data.id) .is('deletedAt', null); - const { data } = await ctx.supabase + const { data } = await ctx.db .rpc('shift_list_index', { board_id: list.data.boardId, list_index: list.data.index @@ -132,7 +132,7 @@ export const listRouter = createTRPCRouter({ if (!userId) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('list') .update({ name: input.name }) .eq('publicId', input.listPublicId) diff --git a/src/server/api/routers/workspace.ts b/src/server/api/routers/workspace.ts index 9e4e4f79..40b8330b 100644 --- a/src/server/api/routers/workspace.ts +++ b/src/server/api/routers/workspace.ts @@ -13,7 +13,7 @@ export const workspaceRouter = createTRPCRouter({ if (!userId) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('workspace_members') .select(` role, @@ -34,7 +34,7 @@ export const workspaceRouter = createTRPCRouter({ if (!userId) return; - const { data } = await ctx.supabase + const { data } = await ctx.db .from('workspace') .select(` publicId, @@ -66,7 +66,7 @@ export const workspaceRouter = createTRPCRouter({ if (!userId) return; - const workspace = await ctx.supabase + const workspace = await ctx.db .from('workspace') .insert({ publicId: generateUID(), @@ -86,7 +86,7 @@ export const workspaceRouter = createTRPCRouter({ if (!newWorkspaceId) return; - await ctx.supabase + await ctx.db .from('workspace_members') .insert({ publicId: generateUID(), diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts index b6f1235a..b15a09ed 100644 --- a/src/server/api/trpc.ts +++ b/src/server/api/trpc.ts @@ -11,10 +11,9 @@ import { type CreateNextContextOptions } from "@trpc/server/adapters/next"; import superjson from "superjson"; import { ZodError } from "zod"; -import { db } from "~/server/db"; import createClient from "~/utils/supabase/api"; import { type Database } from "~/types/database.types"; -import { SupabaseClient } from '@supabase/supabase-js'; +import { type SupabaseClient } from '@supabase/supabase-js'; /** * 1. CONTEXT @@ -30,8 +29,7 @@ type User = { interface CreateContextOptions { user: User | null; - supabase: SupabaseClient - db: any; + db: SupabaseClient } /** @@ -48,8 +46,7 @@ interface CreateContextOptions { export const createInnerTRPCContext = (opts: CreateContextOptions) => { return { user: opts.user, - supabase: opts.supabase, - db, + db: opts.db, }; }; @@ -60,11 +57,11 @@ export const createInnerTRPCContext = (opts: CreateContextOptions) => { * @see https://trpc.io/docs/context */ export const createTRPCContext = async (_opts: CreateNextContextOptions) => { - const supabase = createClient(_opts.req, _opts.res); + const db = createClient(_opts.req, _opts.res); - const { data: { user } } = await supabase.auth.getUser() + const { data: { user } } = await db.auth.getUser() - return createInnerTRPCContext({ user, supabase }); + return createInnerTRPCContext({ user, db }); }; /** diff --git a/src/server/auth.ts b/src/server/auth.ts deleted file mode 100644 index 3c72b498..00000000 --- a/src/server/auth.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { render } from '@react-email/render'; -import { MagicLinkEmail } from "~/email/emails/magic-link"; - -import { DrizzleAdapter } from "@auth/drizzle-adapter"; -import NextAuth, { type DefaultSession } from "next-auth"; - -import { db } from "~/server/db"; -import { pgTable} from "drizzle-orm/pg-core"; - -/** - * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` - * object and keep type safety. - * - * @see https://next-auth.js.org/getting-started/typescript#module-augmentation - */ -declare module "next-auth" { - interface Session extends DefaultSession { - user: { - id: string; - // ...other properties - // role: UserRole; - } & DefaultSession["user"]; - } - - // interface User { - // // ...other properties - // // role: UserRole; - // } -} - - - -export const { - handlers: { GET, POST }, - auth, -} = NextAuth({ - callbacks: { - session: ({ session, user }) => ({ - ...session, - user: { - id: user.id, - name: session.user.name, - email: session.user.email, - image: session.user.image, - }, - }), - }, - adapter: DrizzleAdapter(db, pgTable), - pages: { - signIn: "/auth/login", - // signOut: "/auth/signout", - // error: "/auth/error", - verifyRequest: "/auth/verify", - }, - providers: [ - { - id: 'email', - type: 'email', - from: process.env.EMAIL_FROM ?? '', - server: {}, - maxAge: 24 * 60 * 60, - name: 'Email', - options: {}, - async sendVerificationRequest({ identifier: email, url }) { - const magicLinkEmail = MagicLinkEmail({ loginUrl: url }); - const response = await fetch(process.env.EMAIL_URL ?? '', { - method: "POST", - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${process.env.EMAIL_TOKEN}`, - }, - body: JSON.stringify({ - from: process.env.EMAIL_FROM, - to: [email], - subject: 'Login via email', - html: render(magicLinkEmail), - }), - }) - - if (response.status !== 200) { - throw new Error('Error sending magic login') - } - }, - } - ], -}); diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index a9535b79..3af3e6b3 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -12,8 +12,6 @@ import { bigint, } from "drizzle-orm/pg-core"; -import { type AdapterAccount } from "@auth/core/adapters"; - export const importSourceEnum = pgEnum('source', ['trello']); export const importStatusEnum = pgEnum('status', ['started', 'success', 'failed']); export const memberRoleEnum = pgEnum('role', ['admin', 'member', 'guest']); @@ -236,7 +234,6 @@ export const users = pgTable("user", { }); export const usersRelations = relations(users, ({ many }) => ({ - accounts: many(accounts), boards: many(boards), cards: many(cards), imports: many(imports), @@ -244,61 +241,6 @@ export const usersRelations = relations(users, ({ many }) => ({ workspaces: many(workspaces), })); -export const accounts = pgTable( - "account", - { - userId: uuid("userId") - .notNull() - .references(() => users.id), - type: varchar("type", { length: 255 }) - .$type() - .notNull(), - provider: varchar("provider", { length: 255 }).notNull(), - providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(), - refresh_token: text("refresh_token"), - access_token: text("access_token"), - expires_at: integer("expires_at"), - token_type: varchar("token_type", { length: 255 }), - scope: varchar("scope", { length: 255 }), - id_token: text("id_token"), - session_state: varchar("session_state", { length: 255 }), - }, - (account) => ({ - compoundKey: primaryKey(account.provider, account.providerAccountId), - }) -); - -export const accountsRelations = relations(accounts, ({ one }) => ({ - user: one(users, { fields: [accounts.userId], references: [users.id] }), -})); - -export const sessions = pgTable( - "session", - { - sessionToken: varchar("sessionToken", { length: 255 }) - .notNull() - .primaryKey(), - userId: uuid("userId").notNull().references(() => users.id), - expires: timestamp("expires", { mode: "date" }).notNull(), - }, -); - -export const sessionsRelations = relations(sessions, ({ one }) => ({ - user: one(users, { fields: [sessions.userId], references: [users.id] }), -})); - -export const verificationTokens = pgTable( - "verificationToken", - { - identifier: varchar("identifier", { length: 255 }).notNull(), - token: varchar("token", { length: 255 }).notNull(), - expires: timestamp("expires", { mode: "date" }).notNull(), - }, - (vt) => ({ - compoundKey: primaryKey(vt.identifier, vt.token), - }) -); - export const workspaces = pgTable( "workspace", { diff --git a/src/types/database.types.ts b/src/types/database.types.ts index 729be202..2882ecfb 100644 --- a/src/types/database.types.ts +++ b/src/types/database.types.ts @@ -600,7 +600,17 @@ export type Database = { [_ in never]: never } Functions: { - reorder_list: { + reorder_cards: { + Args: { + card_id: number + current_list_id: number + new_list_id: number + current_index: number + new_index: number + } + Returns: undefined + } + reorder_lists: { Args: { board_id: number list_id: number @@ -609,6 +619,13 @@ export type Database = { } Returns: undefined } + shift_card_index: { + Args: { + list_id: number + card_index: number + } + Returns: undefined + } shift_list_index: { Args: { board_id: number diff --git a/supabase/seed.sql b/supabase/seed.sql index bdfd1c67..f44c2e14 100644 --- a/supabase/seed.sql +++ b/supabase/seed.sql @@ -1,4 +1,4 @@ -CREATE OR REPLACE FUNCTION reorder_list(board_id BIGINT, list_id BIGINT, current_index INT, new_index INT) +CREATE OR REPLACE FUNCTION reorder_lists(board_id BIGINT, list_id BIGINT, current_index INT, new_index INT) RETURNS VOID LANGUAGE SQL AS $$ @@ -13,6 +13,41 @@ AS $$ WHERE "boardId" = board_id; $$; +CREATE OR REPLACE FUNCTION reorder_cards(card_id BIGINT, current_list_id BIGINT, new_list_id BIGINT, current_index INT, new_index INT) +RETURNS VOID +LANGUAGE PLPGSQL +AS $$ + DECLARE + card_index INT; + BEGIN + SELECT index INTO card_index FROM card WHERE "listId" = current_list_id AND id = card_id AND "deletedAt" IS NULL; + + IF current_list_id = new_list_id THEN + UPDATE card + SET index = + CASE + WHEN index = current_index THEN new_index + WHEN current_index < new_index AND index > current_index AND index <= new_index THEN index - 1 + WHEN current_index > new_index AND index >= new_index AND index < current_index THEN index + 1 + ELSE index + END + WHERE "listId" = current_list_id AND "deletedAt" IS NULL; + ELSE + UPDATE card + SET index = index + 1 + WHERE "listId" = new_list_id AND index >= new_index AND "deletedAt" IS NULL; + + UPDATE card + SET index = index - 1 + WHERE "listId" = current_list_id AND index >= current_index AND "deletedAt" IS NULL; + + UPDATE card + SET "listId" = new_list_id, index = new_index + WHERE id = card_id AND "deletedAt" IS NULL; + END IF; + END; +$$ + CREATE OR REPLACE FUNCTION shift_list_index(board_id BIGINT, list_index INT) RETURNS VOID LANGUAGE SQL @@ -20,4 +55,14 @@ AS $$ UPDATE list SET index = index - 1 WHERE "boardId" = board_id AND index > list_index AND "deletedAt" IS NULL; -$$; \ No newline at end of file +$$; + +CREATE OR REPLACE FUNCTION shift_card_index(list_id BIGINT, card_index INT) +RETURNS VOID +LANGUAGE SQL +AS $$ + UPDATE card + SET index = index - 1 + WHERE "listId" = list_id AND index > card_index AND "deletedAt" IS NULL; +$$; +