feat: switch next auth and drizzle to supabase (#1)

* feat: add supabase

* feat: setup auth

* feat: test refactoring board queries

* feat: convert to pages router

* feat: convert board router to supabase

* feat: swap out drizzle for supabase in label, list and workspace routers

* feat: swap out drizzle for supabase on the import router

* feat: swap drizzle for supabase on create new card

* feat: switch card router to use supabase

* feat: finish swapping drizzle for supabase

* chore: lint all router files

* fix: signout

* chore: fix types
This commit is contained in:
Henry
2024-04-23 11:57:34 +01:00
committed by GitHub
parent 1711df03e7
commit d15870118d
96 changed files with 3636 additions and 1521 deletions

144
src/providers/board.tsx Normal file
View File

@@ -0,0 +1,144 @@
import React, {
createContext,
useContext,
useState,
type ReactNode,
} from "react";
import { api } from "~/utils/api";
interface BoardContextProps {
boardData: BoardData;
setBoardData: React.Dispatch<React.SetStateAction<BoardData>>;
updateList: (params: UpdateListParams) => void;
updateCard: (params: UpdateCardParams) => void;
}
interface BoardData {
name: string;
publicId: string;
labels: Label[];
lists: List[];
workspace: {
members: Members[];
} | null;
}
interface Label {
publicId: string;
name: string;
colourCode: string | null;
}
interface List {
publicId: string;
name: string;
boardId: number;
index: number;
cards: Card[];
}
interface Members {
publicId: string;
user: {
name: string | null;
} | null;
}
interface Card {
publicId: string;
title: string;
}
interface UpdateListParams {
boardId: string;
listId: string;
currentIndex: number;
newIndex: number;
}
interface UpdateCardParams {
cardId: string;
newListId: string;
newIndex: number;
}
const initialBoardData: BoardData = {
name: "",
publicId: "",
lists: [],
labels: [],
workspace: {
members: [],
},
};
const BoardContext = createContext<BoardContextProps | undefined>(undefined);
export const BoardProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const utils = api.useUtils();
const [boardData, setBoardData] = useState<BoardData>(initialBoardData);
const refetchBoard = () =>
utils.board.byId.refetch({ id: boardData.publicId });
const updateCardMutation = api.card.reorder.useMutation({
onSuccess: async () => {
try {
await refetchBoard();
} catch (e) {
console.log(e);
}
},
});
const updateListMutation = api.list.reorder.useMutation({
onSuccess: async () => {
try {
await refetchBoard();
} catch (e) {
console.log(e);
}
},
});
const updateList = ({
boardId,
listId,
currentIndex,
newIndex,
}: UpdateListParams) => {
updateListMutation.mutate({
boardId,
listId,
currentIndex,
newIndex,
});
};
const updateCard = ({ cardId, newListId, newIndex }: UpdateCardParams) => {
updateCardMutation.mutate({
cardId,
newListId,
newIndex,
});
};
return (
<BoardContext.Provider
value={{ boardData, setBoardData, updateList, updateCard }}
>
{children}
</BoardContext.Provider>
);
};
export const useBoard = (): BoardContextProps => {
const context = useContext(BoardContext);
if (!context) {
throw new Error("useBoard must be used within a BoardProvider");
}
return context;
};

44
src/providers/modal.tsx Normal file
View File

@@ -0,0 +1,44 @@
import { createContext, useContext, useState } from "react";
type ModalContextType = {
isOpen: boolean;
openModal: (contentType: string) => void;
closeModal: () => void;
modalContentType: string;
};
interface Props {
children: React.ReactNode;
}
const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const ModalProvider: React.FC<Props> = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const [modalContentType, setModalContentType] = useState("");
const openModal = (contentType: string) => {
setIsOpen(true);
setModalContentType(contentType);
};
const closeModal = () => {
setIsOpen(false);
};
return (
<ModalContext.Provider
value={{ isOpen, openModal, closeModal, modalContentType }}
>
{children}
</ModalContext.Provider>
);
};
export const useModal = () => {
const context = useContext(ModalContext);
if (context === undefined) {
throw new Error("useModal must be used within a ModalProvider");
}
return context;
};

68
src/providers/theme.tsx Normal file
View File

@@ -0,0 +1,68 @@
import React, {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
interface ThemeContextProps {
theme: string;
switchTheme: (theme: string) => void;
}
const ThemeContext = createContext<ThemeContextProps | undefined>(undefined);
export const ThemeProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const [theme, setTheme] = useState("");
const switchTheme = (theme: string) => {
if (theme === "system") {
localStorage.removeItem("theme");
} else {
if (theme === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
localStorage.theme = theme;
}
setTheme(theme);
};
useEffect(() => {
if (!("theme" in localStorage)) {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
setTheme("system");
} else {
if (localStorage.theme === "dark") {
document.documentElement.classList.add("dark");
setTheme("dark");
} else {
document.documentElement.classList.remove("dark");
setTheme("light");
}
}
}, []);
return (
<ThemeContext.Provider value={{ switchTheme, theme }}>
{theme.length ? children : null}
</ThemeContext.Provider>
);
};
export const useTheme = (): ThemeContextProps => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};

113
src/providers/workspace.tsx Normal file
View File

@@ -0,0 +1,113 @@
import React, {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
import { api } from "~/utils/api";
import { useRouter } from "next/navigation";
interface WorkspaceContextProps {
workspace: Workspace;
switchWorkspace: (_workspace: Workspace) => void;
availableWorkspaces: Workspace[];
}
interface Workspace {
name: string;
publicId: string;
}
const initialWorkspace: Workspace = {
name: "",
publicId: "",
};
const initialAvailableWorkspaces: Workspace[] = [];
const WorkspaceContext = createContext<WorkspaceContextProps | undefined>(
undefined,
);
export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const router = useRouter();
const [workspace, setWorkspace] = useState<Workspace>(initialWorkspace);
const [availableWorkspaces, setAvailableWorkspaces] = useState<Workspace[]>(
initialAvailableWorkspaces,
);
const { data } = api.workspace.all.useQuery();
const switchWorkspace = (_workspace: Workspace) => {
localStorage.setItem("workspacePublicId", _workspace.publicId);
setWorkspace(_workspace);
router.push(`/boards`);
};
useEffect(() => {
if (!data) return;
const storedWorkspaceId: string | null =
localStorage.getItem("workspacePublicId");
if (data?.length) {
const workspaces = data
.map(({ workspace }) => {
if (!workspace) return;
return {
publicId: workspace.publicId,
name: workspace.name,
};
})
.filter((workspace) => workspace !== null) as Workspace[];
if (workspaces.length) setAvailableWorkspaces(workspaces);
}
if (storedWorkspaceId !== null) {
const newData = data;
const selectedWorkspace = newData?.find(
({ workspace }) => workspace?.publicId === storedWorkspaceId,
);
if (!selectedWorkspace?.workspace) return;
setWorkspace({
publicId: selectedWorkspace.workspace.publicId,
name: selectedWorkspace.workspace.name,
});
} else {
const primaryWorkspace = data?.[0]?.workspace;
if (!primaryWorkspace) return;
localStorage.setItem("workspacePublicId", primaryWorkspace?.publicId);
setWorkspace({
publicId: primaryWorkspace?.publicId,
name: primaryWorkspace?.name,
});
}
}, [data]);
return (
<WorkspaceContext.Provider
value={{ workspace, availableWorkspaces, switchWorkspace }}
>
{children}
</WorkspaceContext.Provider>
);
};
export const useWorkspace = (): WorkspaceContextProps => {
const context = useContext(WorkspaceContext);
if (!context) {
throw new Error("useWorkspace must be used within a WorkspaceProvider");
}
return context;
};