diff --git a/apps/web/app/(app)/smart-categories/board/page.tsx b/apps/web/app/(app)/smart-categories/board/page.tsx
deleted file mode 100644
index b02649f43..000000000
--- a/apps/web/app/(app)/smart-categories/board/page.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import { capitalCase } from "capital-case";
-import { KanbanBoard } from "@/components/kanban/KanbanBoard";
-import { auth } from "@/app/api/auth/[...nextauth]/auth";
-import prisma from "@/utils/prisma";
-import { ClientOnly } from "@/components/ClientOnly";
-import { isDefined } from "@/utils/types";
-import { getUserCategories } from "@/utils/category.server";
-
-export const dynamic = "force-dynamic";
-
-const CATEGORY_ORDER = [
- "Unknown",
- "RequestMoreInformation",
- "Newsletter",
- "Marketing",
- "Receipts",
- "Support",
-];
-
-export default async function CategoriesPage() {
- const session = await auth();
- const email = session?.user.email;
- if (!email) throw new Error("Not authenticated");
-
- const [categories, senders] = await Promise.all([
- getUserCategories(session.user.id),
- prisma.newsletter.findMany({
- where: { userId: session.user.id, categoryId: { not: null } },
- select: { id: true, email: true, categoryId: true },
- }),
- ]);
-
- if (!categories.length) return
No categories found
;
-
- // Order categories
- const orderedCategories = [
- ...CATEGORY_ORDER.map((name) =>
- categories.find((c) => c.name === name),
- ).filter(isDefined),
- ...categories.filter((c) => !CATEGORY_ORDER.includes(c.name)),
- ];
-
- return (
-
-
- ({
- id: c.id,
- title: capitalCase(c.name),
- }))}
- items={senders.map((s) => ({
- id: s.id,
- columnId: s.categoryId || "Uncategorized",
- content: s.email,
- }))}
- />
-
-
- );
-}
diff --git a/apps/web/components/kanban/BoardColumn.tsx b/apps/web/components/kanban/BoardColumn.tsx
deleted file mode 100644
index f2b02abfd..000000000
--- a/apps/web/components/kanban/BoardColumn.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-import { useMemo } from "react";
-import { SortableContext, useSortable } from "@dnd-kit/sortable";
-import { useDndContext, type UniqueIdentifier } from "@dnd-kit/core";
-import { CSS } from "@dnd-kit/utilities";
-import { cva } from "class-variance-authority";
-import { GripVertical } from "lucide-react";
-import { type Task, TaskCard } from "./TaskCard";
-import { Card, CardHeader, CardContent } from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { ScrollBar, ScrollArea } from "@/components/ui/scroll-area";
-
-export interface Column {
- id: UniqueIdentifier;
- title: string;
-}
-
-export type ColumnType = "Column";
-
-export interface ColumnDragData {
- type: ColumnType;
- column: Column;
-}
-
-interface BoardColumnProps {
- column: Column;
- tasks: Task[];
- isOverlay?: boolean;
-}
-
-export function BoardColumn({ column, tasks, isOverlay }: BoardColumnProps) {
- const tasksIds = useMemo(() => {
- return tasks.map((task) => task.id);
- }, [tasks]);
-
- const {
- setNodeRef,
- attributes,
- listeners,
- transform,
- transition,
- isDragging,
- } = useSortable({
- id: column.id,
- data: {
- type: "Column",
- column,
- } satisfies ColumnDragData,
- attributes: {
- roleDescription: `Column: ${column.title}`,
- },
- });
-
- const style = {
- transition,
- transform: CSS.Translate.toString(transform),
- };
-
- const variants = cva(
- "h-[800px] max-h-[800px] w-[350px] max-w-full bg-primary-foreground flex flex-col flex-shrink-0 snap-center",
- {
- variants: {
- dragging: {
- default: "border-2 border-transparent",
- over: "ring-2 opacity-30",
- overlay: "ring-2 ring-primary",
- },
- },
- },
- );
-
- return (
-
-
-
- {column.title}
-
-
-
-
- {tasks.map((task) => (
-
- ))}
-
-
-
-
- );
-}
-
-export function BoardContainer({ children }: { children: React.ReactNode }) {
- const dndContext = useDndContext();
-
- const variations = cva("px-2 md:px-0 flex lg:justify-center pb-4", {
- variants: {
- dragging: {
- default: "snap-x snap-mandatory",
- active: "snap-none",
- },
- },
- });
-
- return (
-
-
- {children}
-
-
-
- );
-}
diff --git a/apps/web/components/kanban/KanbanBoard.tsx b/apps/web/components/kanban/KanbanBoard.tsx
deleted file mode 100644
index 29d67e839..000000000
--- a/apps/web/components/kanban/KanbanBoard.tsx
+++ /dev/null
@@ -1,368 +0,0 @@
-"use client";
-
-// based off of https://github.com/Georgegriff/react-dnd-kit-tailwind-shadcn-ui
-
-import { useMemo, useRef, useState } from "react";
-import { createPortal } from "react-dom";
-
-import { BoardColumn, BoardContainer } from "./BoardColumn";
-import {
- DndContext,
- type DragEndEvent,
- type DragOverEvent,
- DragOverlay,
- type DragStartEvent,
- useSensor,
- useSensors,
- KeyboardSensor,
- type Announcements,
- type UniqueIdentifier,
- TouchSensor,
- MouseSensor,
-} from "@dnd-kit/core";
-import { SortableContext, arrayMove } from "@dnd-kit/sortable";
-import { type Task, TaskCard } from "./TaskCard";
-import type { Column } from "./BoardColumn";
-import { hasDraggableData } from "./kanban-utils";
-import { coordinateGetter } from "./multipleContainersKeyboardPreset";
-
-// const initialTasks: Task[] = [
-// {
-// id: "task1",
-// columnId: "done",
-// content: "Project initiation and planning",
-// },
-// {
-// id: "task2",
-// columnId: "done",
-// content: "Gather requirements from stakeholders",
-// },
-// {
-// id: "task3",
-// columnId: "done",
-// content: "Create wireframes and mockups",
-// },
-// {
-// id: "task4",
-// columnId: "in-progress",
-// content: "Develop homepage layout",
-// },
-// {
-// id: "task5",
-// columnId: "in-progress",
-// content: "Design color scheme and typography",
-// },
-// {
-// id: "task6",
-// columnId: "todo",
-// content: "Implement user authentication",
-// },
-// {
-// id: "task7",
-// columnId: "todo",
-// content: "Build contact us page",
-// },
-// {
-// id: "task8",
-// columnId: "todo",
-// content: "Create product catalog",
-// },
-// {
-// id: "task9",
-// columnId: "todo",
-// content: "Develop about us page",
-// },
-// {
-// id: "task10",
-// columnId: "todo",
-// content: "Optimize website for mobile devices",
-// },
-// {
-// id: "task11",
-// columnId: "todo",
-// content: "Integrate payment gateway",
-// },
-// {
-// id: "task12",
-// columnId: "todo",
-// content: "Perform testing and bug fixing",
-// },
-// {
-// id: "task13",
-// columnId: "todo",
-// content: "Launch website and deploy to server",
-// },
-// ];
-
-export function KanbanBoard({
- categories,
- items,
-}: {
- categories: Column[];
- items: Task[];
-}) {
- const [columns, setColumns] = useState(categories);
- const pickedUpTaskColumn = useRef(null);
- const columnsId = useMemo(() => columns.map((col) => col.id), [columns]);
-
- const [tasks, setTasks] = useState(items);
-
- const [activeColumn, setActiveColumn] = useState(null);
-
- const [activeTask, setActiveTask] = useState(null);
-
- const sensors = useSensors(
- useSensor(MouseSensor),
- useSensor(TouchSensor),
- useSensor(KeyboardSensor, {
- coordinateGetter: coordinateGetter,
- }),
- );
-
- function getDraggingTaskData(taskId: UniqueIdentifier, columnId: string) {
- const tasksInColumn = tasks.filter((task) => task.columnId === columnId);
- const taskPosition = tasksInColumn.findIndex((task) => task.id === taskId);
- const column = columns.find((col) => col.id === columnId);
- return {
- tasksInColumn,
- taskPosition,
- column,
- };
- }
-
- const announcements: Announcements = {
- onDragStart({ active }) {
- if (!hasDraggableData(active)) return;
- if (active.data.current?.type === "Column") {
- const startColumnIdx = columnsId.findIndex((id) => id === active.id);
- const startColumn = columns[startColumnIdx];
- return `Picked up Column ${startColumn?.title} at position: ${
- startColumnIdx + 1
- } of ${columnsId.length}`;
- }
- if (active.data.current?.type === "Task") {
- pickedUpTaskColumn.current = active.data.current.task.columnId;
- const { tasksInColumn, taskPosition, column } = getDraggingTaskData(
- active.id,
- pickedUpTaskColumn.current!, // TODO: !
- );
- return `Picked up Task ${
- active.data.current.task.content
- } at position: ${taskPosition + 1} of ${
- tasksInColumn.length
- } in column ${column?.title}`;
- }
- },
- onDragOver({ active, over }) {
- if (!hasDraggableData(active) || !hasDraggableData(over)) return;
-
- if (
- active.data.current?.type === "Column" &&
- over.data.current?.type === "Column"
- ) {
- const overColumnIdx = columnsId.findIndex((id) => id === over.id);
- return `Column ${active.data.current.column.title} was moved over ${
- over.data.current.column.title
- } at position ${overColumnIdx + 1} of ${columnsId.length}`;
- }
- if (
- active.data.current?.type === "Task" &&
- over.data.current?.type === "Task"
- ) {
- const { tasksInColumn, taskPosition, column } = getDraggingTaskData(
- over.id,
- over.data.current.task.columnId,
- );
- if (over.data.current.task.columnId !== pickedUpTaskColumn.current) {
- return `Task ${
- active.data.current.task.content
- } was moved over column ${column?.title} in position ${
- taskPosition + 1
- } of ${tasksInColumn.length}`;
- }
- return `Task was moved over position ${taskPosition + 1} of ${
- tasksInColumn.length
- } in column ${column?.title}`;
- }
- },
- onDragEnd({ active, over }) {
- if (!hasDraggableData(active) || !hasDraggableData(over)) {
- pickedUpTaskColumn.current = null;
- return;
- }
- if (
- active.data.current?.type === "Column" &&
- over.data.current?.type === "Column"
- ) {
- const overColumnPosition = columnsId.findIndex((id) => id === over.id);
-
- return `Column ${
- active.data.current.column.title
- } was dropped into position ${overColumnPosition + 1} of ${
- columnsId.length
- }`;
- }
- if (
- active.data.current?.type === "Task" &&
- over.data.current?.type === "Task"
- ) {
- const { tasksInColumn, taskPosition, column } = getDraggingTaskData(
- over.id,
- over.data.current.task.columnId,
- );
- if (over.data.current.task.columnId !== pickedUpTaskColumn.current) {
- return `Task was dropped into column ${column?.title} in position ${
- taskPosition + 1
- } of ${tasksInColumn.length}`;
- }
- return `Task was dropped into position ${taskPosition + 1} of ${
- tasksInColumn.length
- } in column ${column?.title}`;
- }
- pickedUpTaskColumn.current = null;
- },
- onDragCancel({ active }) {
- pickedUpTaskColumn.current = null;
- if (!hasDraggableData(active)) return;
- return `Dragging ${active.data.current?.type} cancelled.`;
- },
- };
-
- return (
-
-
-
- {columns.map((col) => (
- task.columnId === col.id)}
- />
- ))}
-
-
-
- {"document" in window &&
- createPortal(
-
- {activeColumn && (
- task.columnId === activeColumn.id,
- )}
- />
- )}
- {activeTask && }
- ,
- document.body,
- )}
-
- );
-
- function onDragStart(event: DragStartEvent) {
- if (!hasDraggableData(event.active)) return;
- const data = event.active.data.current;
- if (data?.type === "Column") {
- setActiveColumn(data.column);
- return;
- }
-
- if (data?.type === "Task") {
- setActiveTask(data.task);
- return;
- }
- }
-
- function onDragEnd(event: DragEndEvent) {
- setActiveColumn(null);
- setActiveTask(null);
-
- const { active, over } = event;
- if (!over) return;
-
- const activeId = active.id;
- const overId = over.id;
-
- if (!hasDraggableData(active)) return;
-
- const activeData = active.data.current;
-
- if (activeId === overId) return;
-
- const isActiveAColumn = activeData?.type === "Column";
- if (!isActiveAColumn) return;
-
- setColumns((columns) => {
- const activeColumnIndex = columns.findIndex((col) => col.id === activeId);
-
- const overColumnIndex = columns.findIndex((col) => col.id === overId);
-
- return arrayMove(columns, activeColumnIndex, overColumnIndex);
- });
- }
-
- function onDragOver(event: DragOverEvent) {
- const { active, over } = event;
- if (!over) return;
-
- const activeId = active.id;
- const overId = over.id;
-
- if (activeId === overId) return;
-
- if (!hasDraggableData(active) || !hasDraggableData(over)) return;
-
- const activeData = active.data.current;
- const overData = over.data.current;
-
- const isActiveATask = activeData?.type === "Task";
- const isOverATask = overData?.type === "Task";
-
- if (!isActiveATask) return;
-
- // Im dropping a Task over another Task
- if (isActiveATask && isOverATask) {
- setTasks((tasks) => {
- const activeIndex = tasks.findIndex((t) => t.id === activeId);
- const overIndex = tasks.findIndex((t) => t.id === overId);
- const activeTask = tasks[activeIndex];
- const overTask = tasks[overIndex];
- if (
- activeTask &&
- overTask &&
- activeTask.columnId !== overTask.columnId
- ) {
- activeTask.columnId = overTask.columnId;
- return arrayMove(tasks, activeIndex, overIndex - 1);
- }
-
- return arrayMove(tasks, activeIndex, overIndex);
- });
- }
-
- const isOverAColumn = overData?.type === "Column";
-
- // Im dropping a Task over a column
- if (isActiveATask && isOverAColumn) {
- setTasks((tasks) => {
- const activeIndex = tasks.findIndex((t) => t.id === activeId);
- const activeTask = tasks[activeIndex];
- if (activeTask) {
- activeTask.columnId = overId as string;
- return arrayMove(tasks, activeIndex, activeIndex);
- }
- return tasks;
- });
- }
- }
-}
diff --git a/apps/web/components/kanban/TaskCard.tsx b/apps/web/components/kanban/TaskCard.tsx
deleted file mode 100644
index 448d22cd5..000000000
--- a/apps/web/components/kanban/TaskCard.tsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import type { UniqueIdentifier } from "@dnd-kit/core";
-import { useSortable } from "@dnd-kit/sortable";
-import { CSS } from "@dnd-kit/utilities";
-import { cva } from "class-variance-authority";
-import { GripVertical } from "lucide-react";
-import { Card, CardHeader } from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { EmailCell } from "@/components/EmailCell";
-
-export interface Task {
- id: UniqueIdentifier;
- columnId: string;
- content: string;
-}
-
-interface TaskCardProps {
- task: Task;
- isOverlay?: boolean;
-}
-
-export type TaskType = "Task";
-
-export interface TaskDragData {
- type: TaskType;
- task: Task;
-}
-
-export function TaskCard({ task, isOverlay }: TaskCardProps) {
- const {
- setNodeRef,
- attributes,
- listeners,
- transform,
- transition,
- isDragging,
- } = useSortable({
- id: task.id,
- data: {
- type: "Task",
- task,
- } satisfies TaskDragData,
- attributes: {
- roleDescription: "Task",
- },
- });
-
- const style = {
- transition,
- transform: CSS.Translate.toString(transform),
- };
-
- const variants = cva("", {
- variants: {
- dragging: {
- over: "ring-2 opacity-30",
- overlay: "ring-2 ring-primary",
- },
- },
- });
-
- return (
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/components/kanban/kanban-utils.ts b/apps/web/components/kanban/kanban-utils.ts
deleted file mode 100644
index 6bc0f1c80..000000000
--- a/apps/web/components/kanban/kanban-utils.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { Active, DataRef, Over } from "@dnd-kit/core";
-import type { ColumnDragData } from "./BoardColumn";
-import type { TaskDragData } from "./TaskCard";
-
-type DraggableData = ColumnDragData | TaskDragData;
-
-export function hasDraggableData(
- entry: T | null | undefined,
-): entry is T & {
- data: DataRef;
-} {
- if (!entry) {
- return false;
- }
-
- const data = entry.data.current;
-
- if (data?.type === "Column" || data?.type === "Task") {
- return true;
- }
-
- return false;
-}
diff --git a/apps/web/components/kanban/multipleContainersKeyboardPreset.ts b/apps/web/components/kanban/multipleContainersKeyboardPreset.ts
deleted file mode 100644
index 3ef439a5a..000000000
--- a/apps/web/components/kanban/multipleContainersKeyboardPreset.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import {
- closestCorners,
- getFirstCollision,
- KeyboardCode,
- type DroppableContainer,
- type KeyboardCoordinateGetter,
-} from "@dnd-kit/core";
-
-const directions: string[] = [
- KeyboardCode.Down,
- KeyboardCode.Right,
- KeyboardCode.Up,
- KeyboardCode.Left,
-];
-
-export const coordinateGetter: KeyboardCoordinateGetter = (
- event,
- { context: { active, droppableRects, droppableContainers, collisionRect } },
-) => {
- if (directions.includes(event.code)) {
- event.preventDefault();
-
- if (!active || !collisionRect) {
- return;
- }
-
- const filteredContainers: DroppableContainer[] = [];
-
- for (const entry of droppableContainers.getEnabled()) {
- if (!entry || entry?.disabled) {
- return;
- }
-
- const rect = droppableRects.get(entry.id);
-
- if (!rect) {
- return;
- }
-
- const data = entry.data.current;
-
- if (data) {
- const { type, children } = data;
-
- if (type === "Column" && children?.length > 0) {
- if (active.data.current?.type !== "Column") {
- return;
- }
- }
- }
-
- switch (event.code) {
- case KeyboardCode.Down:
- if (active.data.current?.type === "Column") {
- return;
- }
- if (collisionRect.top < rect.top) {
- // find all droppable areas below
- filteredContainers.push(entry);
- }
- break;
- case KeyboardCode.Up:
- if (active.data.current?.type === "Column") {
- return;
- }
- if (collisionRect.top > rect.top) {
- // find all droppable areas above
- filteredContainers.push(entry);
- }
- break;
- case KeyboardCode.Left:
- if (collisionRect.left >= rect.left + rect.width) {
- // find all droppable areas to left
- filteredContainers.push(entry);
- }
- break;
- case KeyboardCode.Right:
- // find all droppable areas to right
- if (collisionRect.left + collisionRect.width <= rect.left) {
- filteredContainers.push(entry);
- }
- break;
- }
- }
- const collisions = closestCorners({
- active,
- collisionRect: collisionRect,
- droppableRects,
- droppableContainers: filteredContainers,
- pointerCoordinates: null,
- });
- const closestId = getFirstCollision(collisions, "id");
-
- if (closestId != null) {
- const newDroppable = droppableContainers.get(closestId);
- const newNode = newDroppable?.node.current;
- const newRect = newDroppable?.rect.current;
-
- if (newNode && newRect) {
- return {
- x: newRect.left,
- y: newRect.top,
- };
- }
- }
- }
-
- return undefined;
-};
diff --git a/apps/web/package.json b/apps/web/package.json
index fe2c791e6..ad1ee676f 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -21,9 +21,6 @@
"@asteasolutions/zod-to-openapi": "7.3.0",
"@auth/core": "0.38.0",
"@auth/prisma-adapter": "2.8.0",
- "@dnd-kit/core": "6.3.1",
- "@dnd-kit/sortable": "10.0.0",
- "@dnd-kit/utilities": "3.2.2",
"@formkit/auto-animate": "0.8.2",
"@googleapis/gmail": "12.0.0",
"@googleapis/people": "3.0.9",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a6e4f78d6..3c88a3a78 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -128,15 +128,6 @@ importers:
'@auth/prisma-adapter':
specifier: 2.8.0
version: 2.8.0(@prisma/client@6.5.0(prisma@6.5.0(typescript@5.8.2))(typescript@5.8.2))(nodemailer@6.10.0)
- '@dnd-kit/core':
- specifier: 6.3.1
- version: 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@dnd-kit/sortable':
- specifier: 10.0.0
- version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)
- '@dnd-kit/utilities':
- specifier: 3.2.2
- version: 3.2.2(react@19.1.0)
'@formkit/auto-animate':
specifier: 0.8.2
version: 0.8.2
@@ -1795,12 +1786,6 @@ packages:
'@dnd-kit/core': ^6.0.6
react: '>=16.8.0'
- '@dnd-kit/sortable@10.0.0':
- resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==}
- peerDependencies:
- '@dnd-kit/core': ^6.3.0
- react: '>=16.8.0'
-
'@dnd-kit/sortable@7.0.2':
resolution: {integrity: sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA==}
peerDependencies:
@@ -13642,13 +13627,6 @@ snapshots:
react: 19.1.0
tslib: 2.8.1
- '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
- dependencies:
- '@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@dnd-kit/utilities': 3.2.2(react@19.1.0)
- react: 19.1.0
- tslib: 2.8.1
-
'@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)':
dependencies:
'@dnd-kit/core': 6.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)