chore: update modal design
This commit is contained in:
43
src/components/Button.tsx
Normal file
43
src/components/Button.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = ({ children, isLoading, ...props }: ButtonProps) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="inline-flex items-center 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"
|
||||||
|
disabled={isLoading}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="relative flex items-center justify-center">
|
||||||
|
{isLoading && (
|
||||||
|
<span className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<svg
|
||||||
|
className="h-5 w-5 animate-spin text-white dark:text-dark-800"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span className={isLoading ? "invisible" : "visible"}>{children}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Button;
|
||||||
18
src/components/Input.tsx
Normal file
18
src/components/Input.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React, { forwardRef } from "react";
|
||||||
|
|
||||||
|
const Input = forwardRef<
|
||||||
|
HTMLInputElement,
|
||||||
|
React.InputHTMLAttributes<HTMLInputElement>
|
||||||
|
>(({ ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
ref={ref}
|
||||||
|
className="block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 shadow-sm ring-1 ring-inset ring-light-600 placeholder:text-dark-800 focus:ring-2 focus:ring-inset focus:ring-light-700 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Input.displayName = "Input";
|
||||||
|
|
||||||
|
export default Input;
|
||||||
@@ -12,11 +12,7 @@ interface FormValues {
|
|||||||
export function NewWorkspaceForm() {
|
export function NewWorkspaceForm() {
|
||||||
const { closeModal } = useModal();
|
const { closeModal } = useModal();
|
||||||
const { switchWorkspace } = useWorkspace();
|
const { switchWorkspace } = useWorkspace();
|
||||||
const {
|
const { register, handleSubmit } = useForm<FormValues>();
|
||||||
register,
|
|
||||||
handleSubmit,
|
|
||||||
formState: { errors },
|
|
||||||
} = useForm<FormValues>();
|
|
||||||
|
|
||||||
const createWorkspace = api.workspace.create.useMutation({
|
const createWorkspace = api.workspace.create.useMutation({
|
||||||
onSuccess: (values) => {
|
onSuccess: (values) => {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const Modal: React.FC<Props> = ({ children, modalSize = "sm" }) => {
|
|||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel
|
<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]}`}
|
className={`bg-white/1 relative mt-[25vh] w-full transform rounded-lg border border-light-600 text-left shadow-3xl-light backdrop-blur-[10px] transition-all dark:border-dark-600 dark:bg-dark-100/20 dark:shadow-3xl-dark ${modalSizeMap[modalSize]}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Dialog.Panel>
|
</Dialog.Panel>
|
||||||
|
|||||||
@@ -55,12 +55,12 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
|
|||||||
const position = watch("position");
|
const position = watch("position");
|
||||||
|
|
||||||
const createCard = api.card.create.useMutation({
|
const createCard = api.card.create.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: async () => {
|
||||||
refetchBoard();
|
await refetchBoard();
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: async () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
refetchBoard();
|
await refetchBoard();
|
||||||
showPopup({
|
showPopup({
|
||||||
header: "Unable to create card",
|
header: "Unable to create card",
|
||||||
message: "Please try again later, or contact customer support.",
|
message: "Please try again later, or contact customer support.",
|
||||||
|
|||||||
@@ -36,10 +36,12 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
|
|||||||
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
|
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
|
||||||
|
|
||||||
const createList = api.list.create.useMutation({
|
const createList = api.list.create.useMutation({
|
||||||
onSuccess: () => refetchBoard(),
|
onSuccess: async () => {
|
||||||
onError: () => {
|
await refetchBoard();
|
||||||
|
},
|
||||||
|
onError: async () => {
|
||||||
closeModal();
|
closeModal();
|
||||||
refetchBoard();
|
await refetchBoard();
|
||||||
showPopup({
|
showPopup({
|
||||||
header: "Unable to create list",
|
header: "Unable to create list",
|
||||||
message: "Please try again later, or contact customer support.",
|
message: "Please try again later, or contact customer support.",
|
||||||
@@ -69,32 +71,32 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5">
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="flex w-full items-center justify-between pb-4">
|
<div className="px-5 pt-5">
|
||||||
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
|
<div className="flex w-full items-center justify-between pb-4">
|
||||||
New list
|
<h2 className="text-sm font-bold text-neutral-900 dark:text-dark-1000">
|
||||||
</h2>
|
New list
|
||||||
<button
|
</h2>
|
||||||
className="rounded p-1 hover:bg-light-200 focus:outline-none dark:hover:bg-dark-300"
|
<button
|
||||||
onClick={() => closeModal()}
|
className="rounded p-1 hover:bg-light-200 focus:outline-none dark:hover:bg-dark-300"
|
||||||
>
|
onClick={(e) => {
|
||||||
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
|
e.preventDefault();
|
||||||
</button>
|
closeModal();
|
||||||
</div>
|
}}
|
||||||
|
>
|
||||||
|
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<label
|
|
||||||
htmlFor="list-name"
|
|
||||||
className="block pb-2 text-sm font-normal leading-6 text-neutral-900 dark:text-dark-1000"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
id="list-name"
|
id="list-name"
|
||||||
|
placeholder="List name"
|
||||||
{...register("name", { required: true })}
|
{...register("name", { required: true })}
|
||||||
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"
|
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 className="mt-3 flex items-center justify-end">
|
</div>
|
||||||
|
<div className="mt-12 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">
|
<span className="mr-2 text-xs text-light-900 dark:text-dark-900">
|
||||||
Create more
|
Create more
|
||||||
</span>
|
</span>
|
||||||
@@ -110,7 +112,7 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
|
|||||||
"relative inline-flex h-4 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none",
|
"relative inline-flex h-4 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="sr-only">Create more</span>
|
<span className="sr-only">Create another</span>
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -120,7 +122,8 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
|
|||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-5 sm:mt-6">
|
|
||||||
|
<div>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
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"
|
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"
|
||||||
@@ -128,7 +131,7 @@ export function NewListForm({ boardPublicId }: { boardPublicId: string }) {
|
|||||||
Create list
|
Create list
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function BoardsList() {
|
|||||||
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xxl:grid-cols-5">
|
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 xxl:grid-cols-5">
|
||||||
{data?.map((board) => (
|
{data?.map((board) => (
|
||||||
<Link key={board.publicId} href={`boards/${board.publicId}`}>
|
<Link key={board.publicId} href={`boards/${board.publicId}`}>
|
||||||
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-600 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-100 dark:hover:bg-dark-200">
|
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-600 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100">
|
||||||
<PatternedBackground />
|
<PatternedBackground />
|
||||||
<p className="text-md px-4 font-medium text-neutral-900 dark:text-dark-1000">
|
<p className="text-md px-4 font-medium text-neutral-900 dark:text-dark-1000">
|
||||||
{board.name}
|
{board.name}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Fragment, useState } from "react";
|
import { Fragment, useState } from "react";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { Listbox, Transition } from "@headlessui/react";
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
|
import { useForm, Controller } from "react-hook-form";
|
||||||
|
|
||||||
import { FaTrello } from "react-icons/fa";
|
import { FaTrello } from "react-icons/fa";
|
||||||
import { HiChevronUpDown, HiXMark } from "react-icons/hi2";
|
import { HiChevronUpDown, HiXMark } from "react-icons/hi2";
|
||||||
@@ -8,7 +9,8 @@ import { HiChevronUpDown, HiXMark } from "react-icons/hi2";
|
|||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
|
|
||||||
import { useFormik } from "formik";
|
import Button from "~/components/Button";
|
||||||
|
import Input from "~/components/Input";
|
||||||
|
|
||||||
interface TrelloFormValues {
|
interface TrelloFormValues {
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
@@ -18,77 +20,78 @@ interface TrelloFormValues {
|
|||||||
const sources = [{ source: "Trello" }];
|
const sources = [{ source: "Trello" }];
|
||||||
|
|
||||||
const SelectSource = ({ handleNextStep }: { handleNextStep: () => void }) => {
|
const SelectSource = ({ handleNextStep }: { handleNextStep: () => void }) => {
|
||||||
const formik = useFormik({
|
const { control, handleSubmit } = useForm({
|
||||||
initialValues: {
|
defaultValues: {
|
||||||
source: "Trello",
|
source: "Trello",
|
||||||
},
|
},
|
||||||
onSubmit: () => {
|
|
||||||
handleNextStep();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
const onSubmit = () => {
|
||||||
<form onSubmit={formik.handleSubmit}>
|
handleNextStep();
|
||||||
<label
|
};
|
||||||
htmlFor="name"
|
|
||||||
className="block pb-2 text-sm font-normal leading-6 text-neutral-900 dark:text-dark-1000"
|
|
||||||
>
|
|
||||||
Source
|
|
||||||
</label>
|
|
||||||
<Listbox value={formik.values.source} onChange={formik.handleChange}>
|
|
||||||
{({ open }) => (
|
|
||||||
<>
|
|
||||||
<div className="relative">
|
|
||||||
<Listbox.Button className="focus-ring-light-700 block w-full rounded-md border-0 bg-dark-300 bg-white/5 px-4 py-1.5 text-neutral-900 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6">
|
|
||||||
<span className="flex items-center">
|
|
||||||
<FaTrello />
|
|
||||||
<span className="ml-2 block truncate">
|
|
||||||
{formik.values.source}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
|
||||||
<HiChevronUpDown
|
|
||||||
className="h-5 w-5 text-gray-400"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</Listbox.Button>
|
|
||||||
|
|
||||||
<Transition
|
return (
|
||||||
show={open}
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
as={Fragment}
|
<div className="px-5">
|
||||||
leave="transition ease-in duration-100"
|
<Controller
|
||||||
leaveFrom="opacity-100"
|
name="source"
|
||||||
leaveTo="opacity-0"
|
control={control}
|
||||||
>
|
render={({ field }) => (
|
||||||
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-light-50 py-1 text-base text-neutral-900 shadow-lg ring-1 ring-light-600 ring-opacity-5 focus:outline-none dark:bg-dark-300 dark:text-dark-1000 sm:text-sm">
|
<Listbox {...field}>
|
||||||
{sources.map(({ source }, index) => (
|
{({ open }) => (
|
||||||
<Listbox.Option
|
<>
|
||||||
key={`source_${index}`}
|
<div className="relative">
|
||||||
className="relative cursor-default select-none px-1"
|
<Listbox.Button className="focus-ring-light-700 block w-full rounded-md border-0 bg-dark-300 bg-white/5 px-4 py-1.5 text-neutral-900 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6">
|
||||||
value={source}
|
<span className="flex items-center">
|
||||||
>
|
<FaTrello />
|
||||||
<div className="flex items-center rounded-[5px] p-1 hover:bg-light-200 dark:hover:bg-dark-400">
|
<span className="ml-2 block truncate">
|
||||||
<FaTrello className="ml-1" />
|
{field.value}
|
||||||
<span className="ml-2 block truncate font-normal">
|
|
||||||
{source}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</Listbox.Option>
|
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
))}
|
<HiChevronUpDown
|
||||||
</Listbox.Options>
|
className="h-5 w-5 text-gray-400"
|
||||||
</Transition>
|
aria-hidden="true"
|
||||||
</div>
|
/>
|
||||||
</>
|
</span>
|
||||||
)}
|
</Listbox.Button>
|
||||||
</Listbox>
|
|
||||||
<div className="mt-5 sm:mt-6">
|
<Transition
|
||||||
<button
|
show={open}
|
||||||
type="submit"
|
as={Fragment}
|
||||||
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"
|
leave="transition ease-in duration-100"
|
||||||
>
|
leaveFrom="opacity-100"
|
||||||
Select source
|
leaveTo="opacity-0"
|
||||||
</button>
|
>
|
||||||
|
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-light-50 py-1 text-base text-neutral-900 shadow-lg ring-1 ring-light-600 ring-opacity-5 focus:outline-none dark:bg-dark-300 dark:text-dark-1000 sm:text-sm">
|
||||||
|
{sources.map(({ source }, index) => (
|
||||||
|
<Listbox.Option
|
||||||
|
key={`source_${index}`}
|
||||||
|
className="relative cursor-default select-none px-1"
|
||||||
|
value={source}
|
||||||
|
>
|
||||||
|
<div className="flex items-center rounded-[5px] p-1 hover:bg-light-200 dark:hover:bg-dark-400">
|
||||||
|
<FaTrello className="ml-1" />
|
||||||
|
<span className="ml-2 block truncate font-normal">
|
||||||
|
{source}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Listbox.Option>
|
||||||
|
))}
|
||||||
|
</Listbox.Options>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Listbox>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
|
<div>
|
||||||
|
<Button type="submit">Select source</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
@@ -126,121 +129,85 @@ const ImportTrello: React.FC = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const formik = useFormik({
|
const { register, handleSubmit } = useForm<TrelloFormValues>({
|
||||||
initialValues: {
|
defaultValues: {
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
token: "",
|
token: "",
|
||||||
},
|
},
|
||||||
onSubmit: (values: TrelloFormValues) => {
|
|
||||||
handleSetAuthDetails(values.apiKey, values.token);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const boardsFormik = useFormik({
|
const onSubmit = (values: TrelloFormValues) => {
|
||||||
initialValues: {
|
handleSetAuthDetails(values.apiKey, values.token);
|
||||||
...Object.fromEntries(
|
};
|
||||||
|
|
||||||
|
const { register: registerBoards, handleSubmit: handleSubmitBoards } =
|
||||||
|
useForm({
|
||||||
|
defaultValues: Object.fromEntries(
|
||||||
boards?.data?.map((board) => [board.id, true]) ?? [],
|
boards?.data?.map((board) => [board.id, true]) ?? [],
|
||||||
),
|
),
|
||||||
},
|
});
|
||||||
onSubmit: () => {
|
|
||||||
const boardIds = Object.keys(boardsFormik.values).filter(
|
|
||||||
(key) => boardsFormik.values[key] === true,
|
|
||||||
);
|
|
||||||
|
|
||||||
importBoards.mutate({
|
const onSubmitBoards = (values: Record<string, boolean>) => {
|
||||||
boardIds,
|
const boardIds = Object.keys(values).filter((key) => values[key] === true);
|
||||||
apiKey: formik.values.apiKey,
|
|
||||||
token: formik.values.token,
|
importBoards.mutate({
|
||||||
workspacePublicId: workspace?.publicId,
|
boardIds,
|
||||||
});
|
apiKey,
|
||||||
},
|
token,
|
||||||
enableReinitialize: true,
|
workspacePublicId: workspace?.publicId,
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (boards?.data?.length)
|
if (boards?.data?.length)
|
||||||
return (
|
return (
|
||||||
<form onSubmit={boardsFormik.handleSubmit}>
|
<form onSubmit={handleSubmitBoards(onSubmitBoards)}>
|
||||||
<div className="h-[105px] overflow-scroll">
|
<div className="h-[105px] overflow-scroll px-5">
|
||||||
{boards.data.map((board) => (
|
{boards.data.map((board) => (
|
||||||
<div key={board.id}>
|
<div key={board.id}>
|
||||||
<div
|
<label
|
||||||
className="flex items-center rounded-[5px] p-2 hover:bg-dark-300"
|
className="flex cursor-pointer items-center rounded-[5px] p-2 hover:bg-light-100 dark:hover:bg-dark-300"
|
||||||
onClick={async (e) => {
|
htmlFor={board.id}
|
||||||
e.preventDefault();
|
|
||||||
await boardsFormik.setFieldValue(
|
|
||||||
board.id,
|
|
||||||
!boardsFormik.values[board.id],
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id={board.id}
|
id={board.id}
|
||||||
name={board.id}
|
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="h-[14px] w-[14px] rounded bg-transparent"
|
className="h-[14px] w-[14px] rounded bg-transparent ring-0 focus:outline-none focus:ring-0 focus:ring-offset-0"
|
||||||
onClick={(event) => event.stopPropagation()}
|
{...registerBoards(board.id)}
|
||||||
checked={boardsFormik.values[board.id]}
|
|
||||||
/>
|
/>
|
||||||
<label
|
<span className="ml-3 text-sm text-neutral-900 dark:text-dark-1000">
|
||||||
htmlFor={board.id}
|
|
||||||
className="ml-3 text-sm text-dark-1000"
|
|
||||||
>
|
|
||||||
{board.name}
|
{board.name}
|
||||||
</label>
|
</span>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-5 sm:mt-6">
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
<button
|
<div>
|
||||||
type="submit"
|
<Button type="submit" isLoading={importBoards.isPending}>
|
||||||
className="inline-flex w-full justify-center rounded-md bg-dark-1000 px-3 py-2 text-sm font-semibold text-dark-50 shadow-sm focus-visible:outline-none"
|
Import boards
|
||||||
>
|
</Button>
|
||||||
Import selected
|
</div>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
className="text-neutral-900 dark:text-dark-1000"
|
className="text-neutral-900 dark:text-dark-1000"
|
||||||
>
|
>
|
||||||
<label
|
<div className="space-y-4 px-5">
|
||||||
htmlFor="apiKey"
|
<Input id="apiKey" placeholder="API key" {...register("apiKey")} />
|
||||||
className="block pb-2 text-sm font-normal leading-6"
|
<Input id="token" placeholder="Token" {...register("token")} />
|
||||||
>
|
</div>
|
||||||
API Key
|
|
||||||
</label>
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
<input
|
<div>
|
||||||
id="apiKey"
|
<Button type="submit" isLoading={boards.isLoading}>
|
||||||
name="apiKey"
|
Fetch boards
|
||||||
className="mb-2 block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset focus:ring-light-700 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
|
</Button>
|
||||||
value={formik.values.apiKey}
|
</div>
|
||||||
onChange={formik.handleChange}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
htmlFor="token"
|
|
||||||
className="block pb-2 text-sm font-normal leading-6"
|
|
||||||
>
|
|
||||||
Token
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="token"
|
|
||||||
name="token"
|
|
||||||
className="mb-2 block w-full rounded-md border-0 bg-dark-300 bg-white/5 py-1.5 shadow-sm ring-1 ring-inset ring-light-600 focus:ring-2 focus:ring-inset focus:ring-light-700 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
|
|
||||||
value={formik.values.token}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
/>
|
|
||||||
<div className="mt-5 sm:mt-6">
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
Fetch boards
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
@@ -251,8 +218,8 @@ export function ImportBoardsForm() {
|
|||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-5">
|
<div>
|
||||||
<div className="flex w-full justify-between pb-4">
|
<div className="flex w-full items-center justify-between px-5 pb-4 pt-5">
|
||||||
<h2 className="text-sm font-medium text-neutral-900 dark:text-dark-1000">
|
<h2 className="text-sm font-medium text-neutral-900 dark:text-dark-1000">
|
||||||
New import
|
New import
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
import { HiXMark } from "react-icons/hi2";
|
import { HiXMark } from "react-icons/hi2";
|
||||||
|
|
||||||
import { useModal } from "~/providers/modal";
|
import { useModal } from "~/providers/modal";
|
||||||
import { useWorkspace } from "~/providers/workspace";
|
import { useWorkspace } from "~/providers/workspace";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { Formik, Form, Field } from "formik";
|
|
||||||
|
|
||||||
import { type NewBoardInput } from "~/types/router.types";
|
import { type NewBoardInput } from "~/types/router.types";
|
||||||
|
|
||||||
export function NewBoardForm() {
|
export function NewBoardForm() {
|
||||||
@@ -14,6 +10,13 @@ export function NewBoardForm() {
|
|||||||
const { closeModal } = useModal();
|
const { closeModal } = useModal();
|
||||||
const { workspace } = useWorkspace();
|
const { workspace } = useWorkspace();
|
||||||
|
|
||||||
|
const { register, handleSubmit } = useForm<NewBoardInput>({
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
workspacePublicId: workspace?.publicId || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const refetchBoards = () => utils.board.all.refetch();
|
const refetchBoards = () => utils.board.all.refetch();
|
||||||
|
|
||||||
const createBoard = api.board.create.useMutation({
|
const createBoard = api.board.create.useMutation({
|
||||||
@@ -23,52 +26,43 @@ export function NewBoardForm() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onSubmit = (data: NewBoardInput) => {
|
||||||
|
createBoard.mutate(data);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="flex w-full items-center justify-between pb-4 text-neutral-900 dark:text-dark-1000">
|
<div className="px-5 pt-5">
|
||||||
<h2 className="text-sm font-bold">New board</h2>
|
<div className="text-neutral-9000 flex w-full items-center justify-between pb-4 dark:text-dark-1000">
|
||||||
<button
|
<h2 className="text-sm font-bold">New board</h2>
|
||||||
className="rounded p-1 hover:bg-light-300 focus:outline-none dark:hover:bg-dark-300"
|
<button
|
||||||
onClick={() => closeModal()}
|
className="hover:bg-li ght-300 rounded p-1 focus:outline-none dark:hover:bg-dark-300"
|
||||||
>
|
onClick={(e) => {
|
||||||
<HiXMark size={18} className="text-light-900 dark:text-dark-900" />
|
e.preventDefault();
|
||||||
</button>
|
closeModal();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<HiXMark size={18} className="dark:text-dark-9000 text-light-900" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="name"
|
||||||
|
placeholder="Name"
|
||||||
|
{...register("name", { required: true })}
|
||||||
|
className="block w-full rounded-md border-0 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:bg-dark-300 dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Formik
|
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
|
||||||
initialValues={{
|
<div>
|
||||||
name: "",
|
<button
|
||||||
workspacePublicId: "",
|
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"
|
||||||
onSubmit={(values: NewBoardInput) => {
|
|
||||||
createBoard.mutate({
|
|
||||||
name: values.name,
|
|
||||||
workspacePublicId: workspace?.publicId,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Form>
|
|
||||||
<label
|
|
||||||
htmlFor="name"
|
|
||||||
className="block pb-2 text-sm font-normal leading-6 text-neutral-900 dark:text-dark-1000"
|
|
||||||
>
|
>
|
||||||
Name
|
Create board
|
||||||
</label>
|
</button>
|
||||||
<Field
|
</div>
|
||||||
id="name"
|
</div>
|
||||||
name="name"
|
</form>
|
||||||
className="block w-full rounded-md border-0 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:bg-dark-300 dark:text-dark-1000 dark:ring-dark-700 dark:focus:ring-dark-700 sm:text-sm sm:leading-6"
|
|
||||||
/>
|
|
||||||
<div className="mt-5 sm:mt-6">
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
Create board
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Formik>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user