diff --git a/web/components/web-view/create-update-link-form.tsx b/web/components/web-view/create-update-link-form.tsx new file mode 100644 index 000000000..3e1d1368c --- /dev/null +++ b/web/components/web-view/create-update-link-form.tsx @@ -0,0 +1,172 @@ +// react +import React from "react"; + +// next +import { useRouter } from "next/router"; + +// swr +import { mutate } from "swr"; + +// react hooks form +import { useForm } from "react-hook-form"; + +// services +import issuesService from "services/issues.service"; + +// fetch keys +import { M_ISSUE_DETAILS } from "constants/fetch-keys"; + +// hooks +import useToast from "hooks/use-toast"; + +// ui +import { PrimaryButton, Input } from "components/ui"; + +// types +import type { linkDetails, IIssueLink } from "types"; + +type Props = { + links?: linkDetails[]; + data?: linkDetails; + onSuccess: () => void; +}; + +export const CreateUpdateLinkForm: React.FC = (props) => { + const { data, links, onSuccess } = props; + + const router = useRouter(); + const { workspaceSlug, projectId, issueId } = router.query; + + const { setToastAlert } = useToast(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues: { + title: "", + url: "", + }, + }); + + const onSubmit = async (formData: IIssueLink) => { + if (!workspaceSlug || !projectId || !issueId) return; + + const payload = { metadata: {}, ...formData }; + + if (!data) + await issuesService + .createIssueLink( + workspaceSlug.toString(), + projectId.toString(), + issueId.toString(), + payload + ) + .then(() => { + onSuccess(); + mutate( + M_ISSUE_DETAILS(workspaceSlug.toString(), projectId.toString(), issueId.toString()) + ); + }) + .catch((err) => { + if (err?.status === 400) + setToastAlert({ + type: "error", + title: "Error!", + message: "This URL already exists for this issue.", + }); + else + setToastAlert({ + type: "error", + title: "Error!", + message: "Something went wrong. Please try again.", + }); + }); + else { + const updatedLinks = links?.map((l) => + l.id === data.id + ? { + ...l, + title: formData.title, + url: formData.url, + } + : l + ); + + mutate( + M_ISSUE_DETAILS(workspaceSlug.toString(), projectId.toString(), issueId.toString()), + (prevData) => ({ ...prevData, issue_link: updatedLinks }), + false + ); + + await issuesService + .updateIssueLink( + workspaceSlug.toString(), + projectId.toString(), + issueId.toString(), + data!.id, + payload + ) + .then(() => { + onSuccess(); + mutate( + M_ISSUE_DETAILS(workspaceSlug.toString(), projectId.toString(), issueId.toString()) + ); + }); + } + }; + + return ( +
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + {data + ? isSubmitting + ? "Updating Link..." + : "Update Link" + : isSubmitting + ? "Adding Link..." + : "Add Link"} + +
+
+ ); +}; diff --git a/web/components/web-view/index.ts b/web/components/web-view/index.ts index 48178dfc6..817f5f2f1 100644 --- a/web/components/web-view/index.ts +++ b/web/components/web-view/index.ts @@ -5,3 +5,6 @@ export * from "./issue-web-view-form"; export * from "./label"; export * from "./sub-issues"; export * from "./issue-attachments"; +export * from "./issue-properties-detail"; +export * from "./issue-link-list"; +export * from "./create-update-link-form"; diff --git a/web/components/web-view/issue-attachments.tsx b/web/components/web-view/issue-attachments.tsx index e2636fc53..ba6523e9b 100644 --- a/web/components/web-view/issue-attachments.tsx +++ b/web/components/web-view/issue-attachments.tsx @@ -29,7 +29,13 @@ import { Label, WebViewModal } from "components/web-view"; // types import type { IIssueAttachment } from "types"; -export const IssueAttachments: React.FC = () => { +type Props = { + allowed: boolean; +}; + +export const IssueAttachments: React.FC = (props) => { + const { allowed } = props; + const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; @@ -89,6 +95,7 @@ export const IssueAttachments: React.FC = () => { const { getRootProps } = useDropzone({ onDrop, maxSize: 5 * 1024 * 1024, + disabled: !allowed || isLoading, }); const { data: attachments } = useSWR( @@ -109,7 +116,9 @@ export const IssueAttachments: React.FC = () => {
{isLoading ? (

Uploading...

diff --git a/web/components/web-view/issue-link-list.tsx b/web/components/web-view/issue-link-list.tsx new file mode 100644 index 000000000..1eca8a9df --- /dev/null +++ b/web/components/web-view/issue-link-list.tsx @@ -0,0 +1,64 @@ +// react +import React, { useState } from "react"; + +// next +import Link from "next/link"; + +// icons +import { LinkIcon, PlusIcon } from "@heroicons/react/24/outline"; + +// components +import { Label, WebViewModal, CreateUpdateLinkForm } from "components/web-view"; + +// ui +import { SecondaryButton } from "components/ui"; + +// types +import type { linkDetails } from "types"; + +type Props = { + allowed: boolean; + links?: linkDetails[]; +}; + +export const IssueLinks: React.FC = (props) => { + const { links, allowed } = props; + + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ setIsOpen(false)} modalTitle="Add Link"> + setIsOpen(false)} /> + + + +
+ {links?.map((link) => ( + + ))} + setIsOpen(true)} + className="w-full !py-2 text-custom-text-300 !text-base flex items-center justify-center" + > + + Add + +
+
+ ); +}; diff --git a/web/components/web-view/issue-properties-detail.tsx b/web/components/web-view/issue-properties-detail.tsx new file mode 100644 index 000000000..44e8a968b --- /dev/null +++ b/web/components/web-view/issue-properties-detail.tsx @@ -0,0 +1,69 @@ +// react +import React from "react"; + +// react hook forms +import { Controller } from "react-hook-form"; + +// ui +import { Icon } from "components/ui"; + +// components +import { Label, StateSelect, PrioritySelect } from "components/web-view"; + +// types +import type { IIssue } from "types"; + +type Props = { + control: any; + submitChanges: (data: Partial) => Promise; +}; + +export const IssuePropertiesDetail: React.FC = (props) => { + const { control, submitChanges } = props; + + return ( +
+ +
+
+
+ + State +
+
+ ( + submitChanges({ state: val })} + /> + )} + /> +
+
+
+
+
+
+ + Priority +
+
+ ( + submitChanges({ priority: val })} + /> + )} + /> +
+
+
+
+ ); +}; diff --git a/web/components/web-view/web-view-modal.tsx b/web/components/web-view/web-view-modal.tsx index 347cb3ada..980ddc79a 100644 --- a/web/components/web-view/web-view-modal.tsx +++ b/web/components/web-view/web-view-modal.tsx @@ -36,7 +36,7 @@ export const WebViewModal = (props: Props) => {
-
+
{ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
, HTMLLabelElement> -> = (props) => ( - -); - const MobileWebViewIssueDetail = () => { const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; - const { memberRole } = useProjectMyMembership(); + const memberRole = useProjectMembers( + workspaceSlug as string, + projectId as string, + !!workspaceSlug && !!projectId + ); - const isAllowed = memberRole.isMember || memberRole.isOwner; + const isAllowed = Boolean(memberRole.isMember || memberRole.isOwner); const { user } = useUser(); @@ -161,51 +157,11 @@ const MobileWebViewIssueDetail = () => { -
- -
-
-
- - State -
-
- ( - submitChanges({ state: val })} - /> - )} - /> -
-
-
-
-
-
- - Priority -
-
- ( - submitChanges({ priority: val })} - /> - )} - /> -
-
-
-
+ - + + +
);