diff --git a/src/app/_components/dashboard.tsx b/src/app/_components/dashboard.tsx
new file mode 100644
index 00000000..c14f0ddc
--- /dev/null
+++ b/src/app/_components/dashboard.tsx
@@ -0,0 +1,65 @@
+import Image from "next/image";
+import { redirect } from "next/navigation";
+
+import { getServerAuthSession } from "~/server/auth";
+
+import boardsIcon from "~/app/assets/boards.json";
+
+import ReactiveButton from "~/app/components/ReactiveButton";
+
+const navigation = [
+ { name: "Boards", href: "/boards", icon: boardsIcon, current: true },
+];
+
+export default async function Layout(props: { children: React.ReactNode }) {
+ const session = await getServerAuthSession();
+
+ if (!session?.user) redirect("api/auth/signin");
+
+ return (
+
+
+
+
+
+
{props.children}
+
+
+ );
+}
diff --git a/src/app/boards/[...id]/page.tsx b/src/app/boards/[...id]/page.tsx
index 3da7b957..e0cf12e8 100644
--- a/src/app/boards/[...id]/page.tsx
+++ b/src/app/boards/[...id]/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-
+import Link from "next/link";
import { useParams } from "next/navigation";
import { HiOutlinePlusSmall } from "react-icons/hi2";
import {
@@ -180,17 +180,16 @@ export default function BoardPage() {
index={index}
>
{(provided) => (
-
+ {card.title}
+
)}
))}
diff --git a/src/app/boards/layout.tsx b/src/app/boards/layout.tsx
index c14f0ddc..a49b1cf0 100644
--- a/src/app/boards/layout.tsx
+++ b/src/app/boards/layout.tsx
@@ -1,65 +1,5 @@
-import Image from "next/image";
-import { redirect } from "next/navigation";
+import Dashboard from "~/app/_components/dashboard";
-import { getServerAuthSession } from "~/server/auth";
-
-import boardsIcon from "~/app/assets/boards.json";
-
-import ReactiveButton from "~/app/components/ReactiveButton";
-
-const navigation = [
- { name: "Boards", href: "/boards", icon: boardsIcon, current: true },
-];
-
-export default async function Layout(props: { children: React.ReactNode }) {
- const session = await getServerAuthSession();
-
- if (!session?.user) redirect("api/auth/signin");
-
- return (
-
-
-
-
-
-
{props.children}
-
-
- );
+export default function Layout(props: { children: React.ReactNode }) {
+ return {props.children};
}
diff --git a/src/app/cards/[...id]/page.tsx b/src/app/cards/[...id]/page.tsx
new file mode 100644
index 00000000..6bee5f58
--- /dev/null
+++ b/src/app/cards/[...id]/page.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import { useParams } from "next/navigation";
+import { HiOutlinePlusSmall } from "react-icons/hi2";
+import { Formik, Form, Field, type FieldProps } from "formik";
+
+import { api } from "~/trpc/react";
+
+interface FormValues {
+ cardId: string;
+ description: string;
+}
+
+export default function CardPage() {
+ const params = useParams();
+
+ const cardId = params?.id?.length && params.id[0];
+
+ if (!cardId) return <>>;
+
+ const { data } = api.card.byId.useQuery({ id: cardId });
+
+ const updateCard = api.card.updateDescription.useMutation();
+
+ return (
+
+
+
+ {data?.title}
+
+
+
+
+
+
+
{
+ updateCard.mutate({
+ cardId: values.cardId,
+ description: values.description,
+ });
+ }}
+ enableReinitialize
+ >
+
+
+
+
+ );
+}
diff --git a/src/app/cards/layout.tsx b/src/app/cards/layout.tsx
new file mode 100644
index 00000000..a49b1cf0
--- /dev/null
+++ b/src/app/cards/layout.tsx
@@ -0,0 +1,5 @@
+import Dashboard from "~/app/_components/dashboard";
+
+export default function Layout(props: { children: React.ReactNode }) {
+ return {props.children};
+}
diff --git a/src/server/api/routers/card.ts b/src/server/api/routers/card.ts
index 6f5c715f..7468af51 100644
--- a/src/server/api/routers/card.ts
+++ b/src/server/api/routers/card.ts
@@ -51,48 +51,68 @@ export const cardRouter = createTRPCRouter({
});
})
}),
- update: publicProcedure
- .input(
- z.object({
- cardId: z.string().min(12),
- currentListId: z.string().min(12),
- newListId: z.string().min(12),
- currentIndex: z.number(),
- newIndex: z.number()
- }))
- .mutation(({ ctx, input }) => {
- const userId = ctx.session?.user.id;
+ byId: publicProcedure
+ .input(z.object({ id: z.string().min(12) }))
+ .query(({ ctx, input }) =>
+ ctx.db.query.cards.findFirst({
+ where: eq(cards.publicId, input.id),
+ })
+ ),
+ updateDescription: publicProcedure
+ .input(
+ z.object({
+ cardId: z.string(),
+ description: z.string(),
+ }))
+ .mutation(({ ctx, input }) => {
+ const userId = ctx.session?.user.id;
- if (!userId) return;
+ if (!userId) return;
- return ctx.db.transaction(async (tx) => {
- const [currentList] = await tx.select({ id: lists.id }).from(lists).where(eq(lists.publicId, input.currentListId))
- const [newList] = await tx.select({ id: lists.id }).from(lists).where(eq(lists.publicId, input.newListId))
+ return ctx.db.update(cards).set({ description: input.description}).where(eq(cards.publicId, input.cardId));
+ }),
+ update: publicProcedure
+ .input(
+ z.object({
+ cardId: z.string().min(12),
+ currentListId: z.string().min(12),
+ newListId: z.string().min(12),
+ currentIndex: z.number(),
+ newIndex: z.number()
+ }))
+ .mutation(({ ctx, input }) => {
+ const userId = ctx.session?.user.id;
- if (!currentList?.id || !newList?.id) return;
+ if (!userId) return;
- if (currentList.id === newList.id) {
- await tx.execute(sql`
- UPDATE ${cards}
- SET ${cards.index} =
- CASE
- WHEN ${cards.index} = ${input.currentIndex} THEN ${input.newIndex}
- WHEN ${input.currentIndex} < ${input.newIndex} AND ${cards.index} > ${input.currentIndex} AND ${cards.index} <= ${input.newIndex} THEN ${cards.index} - 1
- WHEN ${input.currentIndex} > ${input.newIndex} AND ${cards.index} >= ${input.newIndex} AND ${cards.index} < ${input.currentIndex} THEN ${cards.index} + 1
- ELSE ${cards.index}
- END
- WHERE ${cards.listId} = ${currentList.id};
- `);
- } else {
- await tx.execute(sql`UPDATE ${cards} SET ${cards.index} = ${cards.index} + 1 WHERE ${cards.listId} = ${newList.id} AND ${cards.index} >= ${input.newIndex};`)
+ return ctx.db.transaction(async (tx) => {
+ const [currentList] = await tx.select({ id: lists.id }).from(lists).where(eq(lists.publicId, input.currentListId))
+ const [newList] = await tx.select({ id: lists.id }).from(lists).where(eq(lists.publicId, input.newListId))
- await tx.execute(sql`UPDATE ${cards} SET ${cards.index} = ${cards.index} - 1 WHERE ${cards.listId} = ${currentList.id} AND ${cards.index} >= ${input.currentIndex};`)
+ if (!currentList?.id || !newList?.id) return;
- await tx
- .update(cards)
- .set({ listId: newList.id, index: input.newIndex })
- .where(eq(cards.publicId, input.cardId));
- }
- })
- })
+ if (currentList.id === newList.id) {
+ await tx.execute(sql`
+ UPDATE ${cards}
+ SET ${cards.index} =
+ CASE
+ WHEN ${cards.index} = ${input.currentIndex} THEN ${input.newIndex}
+ WHEN ${input.currentIndex} < ${input.newIndex} AND ${cards.index} > ${input.currentIndex} AND ${cards.index} <= ${input.newIndex} THEN ${cards.index} - 1
+ WHEN ${input.currentIndex} > ${input.newIndex} AND ${cards.index} >= ${input.newIndex} AND ${cards.index} < ${input.currentIndex} THEN ${cards.index} + 1
+ ELSE ${cards.index}
+ END
+ WHERE ${cards.listId} = ${currentList.id};
+ `);
+ } else {
+ await tx.execute(sql`UPDATE ${cards} SET ${cards.index} = ${cards.index} + 1 WHERE ${cards.listId} = ${newList.id} AND ${cards.index} >= ${input.newIndex};`)
+
+ await tx.execute(sql`UPDATE ${cards} SET ${cards.index} = ${cards.index} - 1 WHERE ${cards.listId} = ${currentList.id} AND ${cards.index} >= ${input.currentIndex};`)
+
+ await tx
+ .update(cards)
+ .set({ listId: newList.id, index: input.newIndex })
+ .where(eq(cards.publicId, input.cardId));
+ }
+ })
+ })
});
\ No newline at end of file
diff --git a/src/server/db/migrations/0001_warm_jamie_braddock.sql b/src/server/db/migrations/0001_warm_jamie_braddock.sql
new file mode 100644
index 00000000..0ab83c3e
--- /dev/null
+++ b/src/server/db/migrations/0001_warm_jamie_braddock.sql
@@ -0,0 +1,4 @@
+ALTER TABLE `card` DROP CONSTRAINT `card_listId_index_unique`;--> statement-breakpoint
+ALTER TABLE `list` DROP CONSTRAINT `list_boardId_index_unique`;--> statement-breakpoint
+ALTER TABLE `board` MODIFY COLUMN `name` varchar(255) NOT NULL;--> statement-breakpoint
+ALTER TABLE `card` MODIFY COLUMN `description` text;
\ No newline at end of file
diff --git a/src/server/db/migrations/meta/0001_snapshot.json b/src/server/db/migrations/meta/0001_snapshot.json
new file mode 100644
index 00000000..e7274387
--- /dev/null
+++ b/src/server/db/migrations/meta/0001_snapshot.json
@@ -0,0 +1,494 @@
+{
+ "version": "5",
+ "dialect": "mysql",
+ "id": "ab7d0b22-efdc-45b3-92cb-86bb630debe8",
+ "prevId": "f4bbbfaf-8b0d-40fd-9414-9e783f1a8197",
+ "tables": {
+ "account": {
+ "name": "account",
+ "columns": {
+ "userId": {
+ "name": "userId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "userId_idx": {
+ "name": "userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "account_provider_providerAccountId": {
+ "name": "account_provider_providerAccountId",
+ "columns": [
+ "provider",
+ "providerAccountId"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "board": {
+ "name": "board",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "onUpdate": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "board_id": {
+ "name": "board_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "board_publicId_unique": {
+ "name": "board_publicId_unique",
+ "columns": [
+ "publicId"
+ ]
+ }
+ }
+ },
+ "card": {
+ "name": "card",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "onUpdate": true
+ },
+ "listId": {
+ "name": "listId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "index": {
+ "name": "index",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "card_id": {
+ "name": "card_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "card_publicId_unique": {
+ "name": "card_publicId_unique",
+ "columns": [
+ "publicId"
+ ]
+ }
+ }
+ },
+ "list": {
+ "name": "list",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "publicId": {
+ "name": "publicId",
+ "type": "varchar(12)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdBy": {
+ "name": "createdBy",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "onUpdate": true
+ },
+ "boardId": {
+ "name": "boardId",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "index": {
+ "name": "index",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "list_id": {
+ "name": "list_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "list_publicId_unique": {
+ "name": "list_publicId_unique",
+ "columns": [
+ "publicId"
+ ]
+ }
+ }
+ },
+ "session": {
+ "name": "session",
+ "columns": {
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "userId_idx": {
+ "name": "userId_idx",
+ "columns": [
+ "userId"
+ ],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "session_sessionToken": {
+ "name": "session_sessionToken",
+ "columns": [
+ "sessionToken"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "user": {
+ "name": "user",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "timestamp(3)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false,
+ "default": "CURRENT_TIMESTAMP(3)"
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "user_id": {
+ "name": "user_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "columns": [
+ "email"
+ ]
+ }
+ }
+ },
+ "verificationToken": {
+ "name": "verificationToken",
+ "columns": {
+ "identifier": {
+ "name": "identifier",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "verificationToken_identifier_token": {
+ "name": "verificationToken_identifier_token",
+ "columns": [
+ "identifier",
+ "token"
+ ]
+ }
+ },
+ "uniqueConstraints": {}
+ }
+ },
+ "schemas": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
\ No newline at end of file
diff --git a/src/server/db/migrations/meta/_journal.json b/src/server/db/migrations/meta/_journal.json
index 11334eea..10c04f27 100644
--- a/src/server/db/migrations/meta/_journal.json
+++ b/src/server/db/migrations/meta/_journal.json
@@ -8,6 +8,13 @@
"when": 1699896771081,
"tag": "0000_hesitant_nocturne",
"breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "5",
+ "when": 1701122139821,
+ "tag": "0001_warm_jamie_braddock",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts
index 09cbac6b..7396370a 100644
--- a/src/server/db/schema.ts
+++ b/src/server/db/schema.ts
@@ -7,7 +7,6 @@ import {
primaryKey,
text,
timestamp,
- unique,
varchar,
} from "drizzle-orm/mysql-core";
import { type AdapterAccount } from "next-auth/adapters";
@@ -76,7 +75,7 @@ export const cards = mySqlTable(
id: bigint("id", { mode: "number" }).primaryKey().autoincrement(),
publicId: varchar("publicId", { length: 12 }).notNull().unique(),
title: varchar("title", { length: 256 }).notNull(),
- description: varchar("description", { length: 256 }),
+ description: text("description"),
createdBy: varchar("createdBy", { length: 256 }).notNull(),
createdAt: timestamp("createdAt")
.default(sql`CURRENT_TIMESTAMP`)