feat: add description on create new card

This commit is contained in:
Henry
2024-08-13 22:57:04 +01:00
parent 18de6f03d8
commit ccdbd9fa14
15 changed files with 267 additions and 200 deletions

265
package-lock.json generated
View File

@@ -56,7 +56,7 @@
"postcss": "^8.4.34",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"supabase": "^1.187.3",
"supabase": "^1.188.4",
"tailwind": "^4.0.0",
"tailwind-scrollbar": "^3.0.5",
"tailwindcss": "^3.4.1",
@@ -737,6 +737,126 @@
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz",
"integrity": "sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz",
"integrity": "sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz",
"integrity": "sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz",
"integrity": "sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz",
"integrity": "sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz",
"integrity": "sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz",
"integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz",
"integrity": "sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"license": "MIT",
@@ -6693,9 +6813,9 @@
}
},
"node_modules/minizlib/node_modules/rimraf": {
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.9.tgz",
"integrity": "sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==",
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
"integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
"dev": true,
"dependencies": {
"glob": "^10.3.7"
@@ -6703,9 +6823,6 @@
"bin": {
"rimraf": "dist/esm/bin.mjs"
},
"engines": {
"node": "14 >=14.20 || 16 >=16.20 || >=18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
@@ -9236,16 +9353,16 @@
}
},
"node_modules/supabase": {
"version": "1.187.3",
"resolved": "https://registry.npmjs.org/supabase/-/supabase-1.187.3.tgz",
"integrity": "sha512-44yzHpxMrd88cUKWw3GVPPIUx6oCqgfqmN4Mlxp7a7GeNNZSTe433vmKBcj+ue3E7K08XcIyEX6G1TjhVyto+g==",
"version": "1.188.4",
"resolved": "https://registry.npmjs.org/supabase/-/supabase-1.188.4.tgz",
"integrity": "sha512-ntOhjrBlnMdacbcmGmuv7Q2JuH6oeukc5iCHFtjZxQRmE583p02HdsALEq7KdITEMjFJOvjM4xJ5/32HAYuRQQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"bin-links": "^4.0.3",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
"tar": "7.4.0"
"tar": "7.4.3"
},
"bin": {
"supabase": "bin/supabase"
@@ -9389,9 +9506,9 @@
}
},
"node_modules/tar": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.0.tgz",
"integrity": "sha512-XQs0S8fuAkQWuqhDeCdMlJXDX80D7EOVLDPVFkna9yQfzS+PHKgfxcei0jf6/+QAWcjqrnC8uM3fSAnrQl+XYg==",
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
"dev": true,
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@@ -10124,126 +10241,6 @@
"@react-email/components": "0.0.12",
"react-email": "^1.10.0"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz",
"integrity": "sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz",
"integrity": "sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz",
"integrity": "sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz",
"integrity": "sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz",
"integrity": "sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz",
"integrity": "sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz",
"integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz",
"integrity": "sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
}
}
}

View File

@@ -61,7 +61,7 @@
"postcss": "^8.4.34",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"supabase": "^1.187.3",
"supabase": "^1.188.4",
"tailwind": "^4.0.0",
"tailwind-scrollbar": "^3.0.5",
"tailwindcss": "^3.4.1",

View File

@@ -22,7 +22,9 @@ export default function CheckboxDropdown({
as="div"
className="relative flex w-full flex-wrap items-center text-left"
>
<Menu.Button>{children}</Menu.Button>
<Menu.Button className="focus-visible:outline-none">
{children}
</Menu.Button>
<Transition
as={Fragment}

View File

@@ -5,11 +5,20 @@ import { useModal } from "~/providers/modal";
interface Props {
children: React.ReactNode;
modalSize?: "sm" | "md" | "lg";
}
const Modal: React.FC<Props> = ({ children }) => {
const Modal: React.FC<Props> = ({ children, modalSize = "sm" }) => {
const { isOpen, closeModal } = useModal();
console.log({ modalSize });
const modalSizeMap = {
sm: "max-w-[400px]",
md: "max-w-[550px]",
lg: "max-w-[800px]",
};
return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
@@ -36,7 +45,9 @@ const Modal: React.FC<Props> = ({ children }) => {
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative mt-[25vh] transform rounded-lg border border-light-600 bg-light-50 px-4 pb-4 pt-5 text-left shadow-3xl-light transition-all dark:border-dark-600 dark:bg-dark-100/20 dark:shadow-3xl-dark dark:backdrop-blur-lg sm:mb-8 sm:w-full sm:max-w-sm sm:p-6">
<Dialog.Panel
className={`relative mt-[25vh] w-full transform rounded-lg border border-light-600 bg-dark-1000 bg-light-50 text-left shadow-3xl-light transition-all dark:border-dark-600 dark:bg-dark-100/20 dark:shadow-3xl-dark dark:backdrop-blur-lg ${modalSizeMap[modalSize]}`}
>
{children}
</Dialog.Panel>
</Transition.Child>

View File

@@ -24,7 +24,8 @@ interface BoardContextProps {
updateCard: (params: ReorderCardInput) => void;
addCard: (params: NewCardInput) => void;
addList: (params: NewListInput) => void;
refetchBoard: () => void;
removeCard: (params: { cardPublicId: string }) => void;
refetchBoard: () => Promise<void>;
}
const initialBoardData: GetBoardByIdOutput = {
@@ -66,9 +67,11 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
};
const updateCardMutation = api.card.reorder.useMutation({
onSuccess: () => refetchBoard(),
onError: () => {
refetchBoard();
onSuccess: async () => {
await refetchBoard();
},
onError: async () => {
await refetchBoard();
showPopup({
header: "Unable to update card",
message: "Please try again later, or contact customer support.",
@@ -77,9 +80,11 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
});
const updateListMutation = api.list.reorder.useMutation({
onSuccess: () => refetchBoard(),
onError: () => {
refetchBoard();
onSuccess: async () => {
await refetchBoard();
},
onError: async () => {
await refetchBoard();
showPopup({
header: "Unable to update list",
message: "Please try again later, or contact customer support.",
@@ -105,7 +110,7 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
const updatedLists = boardData.lists.map((list) => {
if (list.publicId === listPublicId) {
const newCard = {
publicId: generateUID(),
publicId: `PLACEHOLDER_${generateUID()}`,
title,
listId: 2,
description: "",
@@ -115,7 +120,7 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
members:
boardData.workspace?.members.filter((member) =>
memberPublicIds.includes(member.publicId),
) || [],
) ?? [],
index: position === "start" ? 0 : list.cards.length,
};
@@ -148,6 +153,19 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
setBoardData({ ...boardData, lists: updatedLists });
};
const removeCard = ({ cardPublicId }: { cardPublicId: string }) => {
if (!boardData) return;
const updatedLists = boardData.lists.map((list) => {
const updatedCards = list.cards.filter(
(card) => card.publicId !== cardPublicId,
);
return { ...list, cards: updatedCards };
});
setBoardData({ ...boardData, lists: updatedLists });
};
const updateList = ({
boardId,
listId,
@@ -179,6 +197,7 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
updateCard,
addCard,
addList,
removeCard,
refetchBoard,
}}
>

View File

@@ -13,6 +13,7 @@ export const cardRouter = createTRPCRouter({
.input(
z.object({
title: z.string().min(1),
description: z.string().max(10000),
listPublicId: z.string().min(12),
labelPublicIds: z.array(z.string().min(12)),
memberPublicIds: z.array(z.string().min(12)),
@@ -56,6 +57,7 @@ export const cardRouter = createTRPCRouter({
const newCard = await cardRepo.create(ctx.db, {
title: input.title,
description: input.description,
createdBy: userId,
listId: list.id,
index,

View File

@@ -6,6 +6,7 @@ export const create = async (
db: SupabaseClient<Database>,
cardInput: {
title: string;
description: string;
createdBy: string;
listId: number;
index: number;
@@ -16,6 +17,7 @@ export const create = async (
.insert({
publicId: generateUID(),
title: cardInput.title,
description: cardInput.description,
createdBy: cardInput.createdBy,
listId: cardInput.listId,
index: cardInput.index,

View File

@@ -24,7 +24,7 @@ export function DeleteBoardConfirmation() {
};
return (
<>
<div className="p-5">
<div className="flex w-full flex-col justify-between pb-4">
<h2 className="text-md pb-4 font-medium text-neutral-900 dark:text-dark-1000">
Are you sure you want to delete this board?
@@ -47,6 +47,6 @@ export function DeleteBoardConfirmation() {
Delete
</button>
</div>
</>
</div>
);
}

View File

@@ -31,7 +31,7 @@ export function DeleteListConfirmation({
});
return (
<>
<div className="p-5">
<div className="flex w-full flex-col justify-between pb-4">
<h2 className="text-md pb-4 font-medium text-neutral-900 dark:text-dark-1000">
Are you sure you want to delete this list?
@@ -58,6 +58,6 @@ export function DeleteListConfirmation({
Delete
</button>
</div>
</>
</div>
);
}

View File

@@ -1,5 +1,6 @@
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import ContentEditable from "react-contenteditable";
import { api } from "~/utils/api";
import {
@@ -39,6 +40,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
useForm<NewCardFormInput>({
defaultValues: {
title: "",
description: "",
listPublicId,
labelPublicIds: [],
memberPublicIds: [],
@@ -53,10 +55,10 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
const position = watch("position");
const createCard = api.card.create.useMutation({
onSuccess: async () => {
onSuccess: () => {
refetchBoard();
},
onError: async () => {
onError: () => {
closeModal();
refetchBoard();
showPopup({
@@ -107,6 +109,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
createCard.mutate({
title: data.title,
description: data.description,
listPublicId: data.listPublicId,
labelPublicIds: data.labelPublicIds,
memberPublicIds: data.memberPublicIds,
@@ -147,32 +150,38 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
const selectedList = formattedLists.find((item) => item.selected);
return (
<>
<div className="flex w-full items-center justify-between pb-4">
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
New card
</h2>
<button
className="rounded p-1 hover:bg-light-200 focus:outline-none dark:hover:bg-dark-300"
onClick={() => closeModal()}
>
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
</button>
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label
htmlFor="title"
className="block pb-2 text-sm font-normal leading-6 text-neutral-900 dark:text-dark-1000"
<form onSubmit={handleSubmit(onSubmit)}>
<div className="px-5 pt-5">
<div className="flex w-full items-center justify-between pb-5">
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
New card
</h2>
<button
className="rounded p-1 hover:bg-light-200 focus:outline-none dark:hover:bg-dark-300"
onClick={(e) => {
closeModal();
e.preventDefault();
}}
>
Title
</label>
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
</button>
</div>
<div>
<input
id="title"
type="text"
{...register("title")}
className="block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 text-neutral-900 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset focus:ring-light-600 dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
placeholder="Card title"
className="block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 text-neutral-900 placeholder-dark-800 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset focus:ring-light-600 dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
/>
</div>
<div className="mt-2 ">
<ContentEditable
placeholder="Add description..."
html={watch("description") || ""}
onChange={(e) => setValue("description", e.target.value)}
className="block min-h-[70px] w-full rounded-md border-0 bg-dark-300 bg-white/5 px-3 py-1.5 text-light-900 text-neutral-900 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset focus:ring-light-600 focus-visible:outline-none dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
/>
</div>
<div className="mt-2 flex space-x-1">
@@ -280,7 +289,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
e.preventDefault();
setValue("position", position === "start" ? "end" : "start");
}}
className="flex h-auto items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-1.5 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500"
className="flex h-auto items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-1.5 py-1 text-left text-xs text-light-800 hover:bg-light-300 focus-visible:outline-none dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500"
>
{position === "start" ? (
<HiOutlineBarsArrowUp size={14} />
@@ -289,8 +298,10 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
)}
</button>
</div>
</div>
<div className="mt-3 flex items-center justify-end">
<div className="mt-5 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
<div className="mr-4 flex items-center justify-end">
<span className="mr-2 text-xs text-light-900 dark:text-dark-900">
Create more
</span>
@@ -315,7 +326,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
</Switch>
</div>
<div className="mt-5 sm:mt-6">
<div>
<button
type="submit"
className="inline-flex w-full justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
@@ -323,7 +334,7 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
Create card
</button>
</div>
</form>
</>
</div>
</form>
);
}

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useEffect } from "react";
import { api } from "~/utils/api";
import { useForm } from "react-hook-form";
@@ -69,7 +69,7 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
};
return (
<>
<div className="p-5">
<div className="flex w-full items-center justify-between pb-4">
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
New list
@@ -129,6 +129,6 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
</button>
</div>
</form>
</>
</div>
);
}

View File

@@ -212,9 +212,19 @@ export default function BoardPage() {
>
{(provided) => (
<Link
onClick={(e) => {
if (
card.publicId.startsWith("PLACEHOLDER")
)
e.preventDefault();
}}
key={card.publicId}
href={`/cards/${card.publicId}`}
className="mb-2 flex !cursor-pointer flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-300 dark:text-dark-1000 dark:hover:bg-dark-400"
className={`mb-2 flex !cursor-pointer flex-col rounded-md border border-light-200 bg-light-50 px-3 py-2 text-sm text-neutral-900 dark:border-dark-200 dark:bg-dark-300 dark:text-dark-1000 dark:hover:bg-dark-400 ${
card.publicId.startsWith("PLACEHOLDER")
? "pointer-events-none"
: ""
}`}
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
@@ -280,7 +290,7 @@ export default function BoardPage() {
</DragDropContext>
)}
</div>
<Modal>
<Modal modalSize={modalContentType === "NEW_CARD" ? "md" : "sm"}>
{modalContentType === "DELETE_BOARD" && <DeleteBoardConfirmation />}
{modalContentType === "DELETE_LIST" && (
<DeleteListConfirmation listPublicId={selectedPublicListId} />

View File

@@ -251,7 +251,7 @@ export function ImportBoardsForm() {
const [step, setStep] = useState(1);
return (
<>
<div className="p-5">
<div className="flex w-full justify-between pb-4">
<h2 className="text-sm font-medium text-neutral-900 dark:text-dark-1000">
New import
@@ -266,6 +266,6 @@ export function ImportBoardsForm() {
{step === 1 && <SelectSource handleNextStep={() => setStep(step + 1)} />}
{step === 2 && <ImportTrello />}
</>
</div>
);
}

View File

@@ -1,6 +1,8 @@
import { useRouter } from "next/navigation";
import { api } from "~/utils/api";
import { useModal } from "~/providers/modal";
import { useBoard } from "~/providers/board";
import { usePopup } from "~/providers/popup";
interface DeleteCardConfirmationProps {
cardPublicId: string;
@@ -13,16 +15,31 @@ export function DeleteCardConfirmation({
}: DeleteCardConfirmationProps) {
const { closeModal } = useModal();
const router = useRouter();
const { removeCard, refetchBoard } = useBoard();
const { showPopup } = usePopup();
const deleteCard = api.card.delete.useMutation({
onSuccess: () => {
closeModal();
router.push(`/boards/${boardPublicId}`);
},
const deleteCardMutation = api.card.delete.useMutation({
onSuccess: () => refetchBoard(),
onError: () =>
showPopup({
header: "Error deleting card",
message: "Please try again later, or contact customer support.",
}),
});
const handleDeleteCard = () => {
removeCard({
cardPublicId,
});
closeModal();
router.push(`/boards/${boardPublicId}`);
deleteCardMutation.mutate({
cardPublicId,
});
};
return (
<>
<div className="p-5">
<div className="flex w-full flex-col justify-between pb-4">
<h2 className="text-md pb-4 font-medium text-neutral-900 dark:text-dark-1000">
Are you sure you want to delete this card?
@@ -39,16 +56,12 @@ export function DeleteCardConfirmation({
Cancel
</button>
<button
onClick={() =>
deleteCard.mutate({
cardPublicId,
})
}
onClick={handleDeleteCard}
className="inline-flex justify-center rounded-md bg-light-1000 px-3 py-2 text-sm font-semibold text-light-50 shadow-sm focus-visible:outline-none dark:bg-dark-1000 dark:text-dark-50"
>
Delete
</button>
</div>
</>
</div>
);
}

View File

@@ -52,7 +52,7 @@ export function NewLabelForm({ cardPublicId }: cardPublicId) {
});
return (
<>
<div className="p-5">
<div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000">
<h2 className="text-sm font-medium">New label</h2>
<button
@@ -158,6 +158,6 @@ export function NewLabelForm({ cardPublicId }: cardPublicId) {
</div>
</Form>
</Formik>
</>
</div>
);
}