mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: added user auth
This commit is contained in:
parent
9075f9441c
commit
cedc884d92
@ -4,7 +4,7 @@ import Link from "next/link";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
// swr
|
// swr
|
||||||
import useSWR from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
// react-beautiful-dnd
|
// react-beautiful-dnd
|
||||||
import { DraggableStateSnapshot } from "react-beautiful-dnd";
|
import { DraggableStateSnapshot } from "react-beautiful-dnd";
|
||||||
// headless ui
|
// headless ui
|
||||||
@ -17,48 +17,49 @@ import issuesService from "services/issues.service";
|
|||||||
import stateService from "services/state.service";
|
import stateService from "services/state.service";
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
// components
|
// components
|
||||||
import { AssigneesList } from "components/ui/avatar";
|
import { CustomSelect, AssigneesList } from "components/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IssueResponse, IUserLite, IWorkspaceMember, Properties } from "types";
|
import { IIssue, IUserLite, IWorkspaceMember, Properties, UserAuth } from "types";
|
||||||
// common
|
// common
|
||||||
import { PRIORITIES } from "constants/";
|
import { PRIORITIES } from "constants/";
|
||||||
import { PROJECT_ISSUES_LIST, STATE_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
import {
|
||||||
|
STATE_LIST,
|
||||||
|
PROJECT_DETAILS,
|
||||||
|
CYCLE_ISSUES,
|
||||||
|
MODULE_ISSUES,
|
||||||
|
PROJECT_ISSUES_LIST,
|
||||||
|
} from "constants/fetch-keys";
|
||||||
import { getPriorityIcon } from "constants/global";
|
import { getPriorityIcon } from "constants/global";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
type?: string;
|
||||||
|
typeId?: string;
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
properties: Properties;
|
properties: Properties;
|
||||||
snapshot?: DraggableStateSnapshot;
|
snapshot?: DraggableStateSnapshot;
|
||||||
assignees: Partial<IUserLite>[] | (Partial<IUserLite> | undefined)[];
|
assignees: Partial<IUserLite>[] | (Partial<IUserLite> | undefined)[];
|
||||||
people: IWorkspaceMember[] | undefined;
|
people: IWorkspaceMember[] | undefined;
|
||||||
handleDeleteIssue?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
partialUpdateIssue: any;
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleBoardIssue: React.FC<Props> = ({
|
const SingleBoardIssue: React.FC<Props> = ({
|
||||||
|
type,
|
||||||
|
typeId,
|
||||||
issue,
|
issue,
|
||||||
properties,
|
properties,
|
||||||
snapshot,
|
snapshot,
|
||||||
assignees,
|
assignees,
|
||||||
people,
|
people,
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
partialUpdateIssue,
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const { data: issues } = useSWR<IssueResponse>(
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)
|
|
||||||
: null,
|
|
||||||
workspaceSlug && projectId
|
|
||||||
? () => issuesService.getIssues(workspaceSlug as string, projectId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: states } = useSWR(
|
const { data: states } = useSWR(
|
||||||
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
|
workspaceSlug && projectId ? STATE_LIST(projectId as string) : null,
|
||||||
workspaceSlug && projectId
|
workspaceSlug && projectId
|
||||||
@ -73,7 +74,25 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalChildren = issues?.results.filter((i) => i.parent === issue.id).length;
|
const partialUpdateIssue = (formData: Partial<IIssue>) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
issuesService
|
||||||
|
.patchIssue(workspaceSlug as string, projectId as string, issue.id, formData)
|
||||||
|
.then((res) => {
|
||||||
|
if (typeId) {
|
||||||
|
mutate(CYCLE_ISSUES(typeId ?? ""));
|
||||||
|
mutate(MODULE_ISSUES(typeId ?? ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -82,7 +101,7 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="group/card relative select-none p-2">
|
<div className="group/card relative select-none p-2">
|
||||||
{handleDeleteIssue && (
|
{handleDeleteIssue && !isNotAllowed && (
|
||||||
<div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100">
|
<div className="absolute top-1.5 right-1.5 z-10 opacity-0 group-hover/card:opacity-100">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -114,15 +133,18 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
as="div"
|
as="div"
|
||||||
value={issue.priority}
|
value={issue.priority}
|
||||||
onChange={(data: string) => {
|
onChange={(data: string) => {
|
||||||
partialUpdateIssue({ priority: data }, issue.id);
|
partialUpdateIssue({ priority: data });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className="group relative flex-shrink-0"
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={`grid cursor-pointer place-items-center rounded px-2 py-1 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
className={`grid ${
|
||||||
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} place-items-center rounded px-2 py-1 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
||||||
issue.priority === "urgent"
|
issue.priority === "urgent"
|
||||||
? "bg-red-100 text-red-600"
|
? "bg-red-100 text-red-600"
|
||||||
: issue.priority === "high"
|
: issue.priority === "high"
|
||||||
@ -171,14 +193,19 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
as="div"
|
as="div"
|
||||||
value={issue.state}
|
value={issue.state}
|
||||||
onChange={(data: string) => {
|
onChange={(data: string) => {
|
||||||
partialUpdateIssue({ state: data }, issue.id);
|
partialUpdateIssue({ state: data });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className="group relative flex-shrink-0"
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button className="flex cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
<Listbox.Button
|
||||||
|
className={`flex ${
|
||||||
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500`}
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
|
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
|
||||||
style={{
|
style={{
|
||||||
@ -218,10 +245,6 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
</Listbox.Options>
|
</Listbox.Options>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="absolute bottom-full right-0 mb-2 z-10 hidden group-hover:block p-2 bg-white shadow-md rounded-md whitespace-nowrap">
|
|
||||||
<h5 className="font-medium mb-1">State</h5>
|
|
||||||
<div>{issue.state_detail.name}</div>
|
|
||||||
</div> */}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Listbox>
|
</Listbox>
|
||||||
@ -242,7 +265,7 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
{properties.sub_issue_count && (
|
{properties.sub_issue_count && (
|
||||||
<div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
<div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||||
{totalChildren} {totalChildren === 1 ? "sub-issue" : "sub-issues"}
|
{issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{properties.assignee && (
|
{properties.assignee && (
|
||||||
@ -255,15 +278,19 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
if (newData.includes(data)) newData.splice(newData.indexOf(data), 1);
|
if (newData.includes(data)) newData.splice(newData.indexOf(data), 1);
|
||||||
else newData.push(data);
|
else newData.push(data);
|
||||||
|
|
||||||
partialUpdateIssue({ assignees_list: newData }, issue.id);
|
partialUpdateIssue({ assignees_list: newData });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className="group relative flex-shrink-0"
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button>
|
<Listbox.Button>
|
||||||
<div className="flex cursor-pointer items-center gap-1 text-xs">
|
<div
|
||||||
|
className={`flex ${
|
||||||
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} items-center gap-1 text-xs`}
|
||||||
|
>
|
||||||
<AssigneesList users={assignees} length={3} />
|
<AssigneesList users={assignees} length={3} />
|
||||||
</div>
|
</div>
|
||||||
</Listbox.Button>
|
</Listbox.Button>
|
||||||
@ -280,9 +307,7 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={person.id}
|
key={person.id}
|
||||||
className={({ active }) =>
|
className={({ active }) =>
|
||||||
`cursor-pointer select-none p-2 ${
|
`cursor-pointer select-none p-2 ${active ? "bg-indigo-50" : "bg-white"}`
|
||||||
active ? "bg-indigo-50" : "bg-white"
|
|
||||||
}`
|
|
||||||
}
|
}
|
||||||
value={person.member.id}
|
value={person.member.id}
|
||||||
>
|
>
|
||||||
@ -329,7 +354,6 @@ const SingleBoardIssue: React.FC<Props> = ({
|
|||||||
</Listbox.Options>
|
</Listbox.Options>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Listbox>
|
</Listbox>
|
||||||
)}
|
)}
|
||||||
|
@ -21,7 +21,7 @@ import { CalendarDaysIcon } from "@heroicons/react/24/outline";
|
|||||||
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember, Properties } from "types";
|
import { IIssue, IWorkspaceMember, Properties, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import {
|
import {
|
||||||
CYCLE_ISSUES,
|
CYCLE_ISSUES,
|
||||||
@ -41,6 +41,7 @@ type Props = {
|
|||||||
properties: Properties;
|
properties: Properties;
|
||||||
editIssue: () => void;
|
editIssue: () => void;
|
||||||
removeIssue?: () => void;
|
removeIssue?: () => void;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleListIssue: React.FC<Props> = ({
|
const SingleListIssue: React.FC<Props> = ({
|
||||||
@ -50,6 +51,7 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
properties,
|
properties,
|
||||||
editIssue,
|
editIssue,
|
||||||
removeIssue,
|
removeIssue,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const [deleteIssue, setDeleteIssue] = useState<IIssue | undefined>();
|
const [deleteIssue, setDeleteIssue] = useState<IIssue | undefined>();
|
||||||
|
|
||||||
@ -86,6 +88,8 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConfirmIssueDeletion
|
<ConfirmIssueDeletion
|
||||||
@ -121,12 +125,15 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
partialUpdateIssue({ priority: data });
|
partialUpdateIssue({ priority: data });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className="group relative flex-shrink-0"
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={`flex cursor-pointer items-center gap-x-2 rounded px-2 py-0.5 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
className={`flex ${
|
||||||
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} items-center gap-x-2 rounded px-2 py-0.5 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
||||||
issue.priority === "urgent"
|
issue.priority === "urgent"
|
||||||
? "bg-red-100 text-red-600"
|
? "bg-red-100 text-red-600"
|
||||||
: issue.priority === "high"
|
: issue.priority === "high"
|
||||||
@ -210,6 +217,7 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
}}
|
}}
|
||||||
maxHeight="md"
|
maxHeight="md"
|
||||||
noChevron
|
noChevron
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{states?.map((state) => (
|
{states?.map((state) => (
|
||||||
<CustomSelect.Option key={state.id} value={state.id}>
|
<CustomSelect.Option key={state.id} value={state.id}>
|
||||||
@ -270,12 +278,17 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
partialUpdateIssue({ assignees_list: newData });
|
partialUpdateIssue({ assignees_list: newData });
|
||||||
}}
|
}}
|
||||||
className="group relative flex-shrink-0"
|
className="group relative flex-shrink-0"
|
||||||
|
disabled={isNotAllowed}
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button>
|
<Listbox.Button>
|
||||||
<div className="flex cursor-pointer items-center gap-1 text-xs">
|
<div
|
||||||
|
className={`flex ${
|
||||||
|
isNotAllowed ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} items-center gap-1 text-xs`}
|
||||||
|
>
|
||||||
<AssigneesList userIds={issue.assignees ?? []} />
|
<AssigneesList userIds={issue.assignees ?? []} />
|
||||||
</div>
|
</div>
|
||||||
</Listbox.Button>
|
</Listbox.Button>
|
||||||
@ -325,7 +338,7 @@ const SingleListIssue: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</Listbox>
|
</Listbox>
|
||||||
)}
|
)}
|
||||||
{type && (
|
{type && !isNotAllowed && (
|
||||||
<CustomMenu width="auto" ellipsis>
|
<CustomMenu width="auto" ellipsis>
|
||||||
<CustomMenu.MenuItem onClick={editIssue}>Edit</CustomMenu.MenuItem>
|
<CustomMenu.MenuItem onClick={editIssue}>Edit</CustomMenu.MenuItem>
|
||||||
{type !== "issue" && (
|
{type !== "issue" && (
|
||||||
|
@ -2,19 +2,19 @@ import React, { useEffect, useRef, useState } from "react";
|
|||||||
|
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
|
// headless ui
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
|
||||||
import type { IProject, IWorkspace } from "types";
|
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// icons
|
// icons
|
||||||
|
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "components/ui";
|
import { Button, Input } from "components/ui";
|
||||||
// types
|
// types
|
||||||
// constants
|
import type { IProject, IWorkspace } from "types";
|
||||||
|
// fetch-keys
|
||||||
import { PROJECTS_LIST } from "constants/fetch-keys";
|
import { PROJECTS_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
type TConfirmProjectDeletionProps = {
|
type TConfirmProjectDeletionProps = {
|
||||||
@ -86,7 +86,7 @@ const ConfirmProjectDeletion: React.FC<TConfirmProjectDeletionProps> = (props) =
|
|||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10"
|
className="relative z-20"
|
||||||
initialFocus={cancelButtonRef}
|
initialFocus={cancelButtonRef}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
@ -102,7 +102,7 @@ const ConfirmProjectDeletion: React.FC<TConfirmProjectDeletionProps> = (props) =
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
|
@ -33,7 +33,7 @@ const ConfirmProjectMemberRemove: React.FC<Props> = ({ isOpen, onClose, data, ha
|
|||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10"
|
className="relative z-20"
|
||||||
initialFocus={cancelButtonRef}
|
initialFocus={cancelButtonRef}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
@ -49,7 +49,7 @@ const ConfirmProjectMemberRemove: React.FC<Props> = ({ isOpen, onClose, data, ha
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
|
@ -13,7 +13,7 @@ import SingleBoard from "components/project/cycles/board-view/single-board";
|
|||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, IIssue, IProjectMember } from "types";
|
import { CycleIssueResponse, IIssue, IProjectMember, UserAuth } from "types";
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
// constants
|
// constants
|
||||||
import { STATE_LIST, CYCLE_ISSUES } from "constants/fetch-keys";
|
import { STATE_LIST, CYCLE_ISSUES } from "constants/fetch-keys";
|
||||||
@ -23,8 +23,6 @@ type Props = {
|
|||||||
members: IProjectMember[] | undefined;
|
members: IProjectMember[] | undefined;
|
||||||
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
||||||
openIssuesListModal: () => void;
|
openIssuesListModal: () => void;
|
||||||
removeIssueFromCycle: (bridgeId: string) => void;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
setPreloadedData: React.Dispatch<
|
setPreloadedData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@ -34,6 +32,7 @@ type Props = {
|
|||||||
| null
|
| null
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CyclesBoardView: React.FC<Props> = ({
|
const CyclesBoardView: React.FC<Props> = ({
|
||||||
@ -41,10 +40,9 @@ const CyclesBoardView: React.FC<Props> = ({
|
|||||||
members,
|
members,
|
||||||
openCreateIssueModal,
|
openCreateIssueModal,
|
||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromCycle,
|
|
||||||
partialUpdateIssue,
|
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
@ -151,10 +149,8 @@ const CyclesBoardView: React.FC<Props> = ({
|
|||||||
: "#000000"
|
: "#000000"
|
||||||
}
|
}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
removeIssueFromCycle={removeIssueFromCycle}
|
|
||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
openCreateIssueModal={openCreateIssueModal}
|
openCreateIssueModal={openCreateIssueModal}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
stateId={
|
stateId={
|
||||||
@ -162,6 +158,7 @@ const CyclesBoardView: React.FC<Props> = ({
|
|||||||
? states?.find((s) => s.name === singleGroup)?.id ?? null
|
? states?.find((s) => s.name === singleGroup)?.id ?? null
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@ import { CustomMenu } from "components/ui";
|
|||||||
// icons
|
// icons
|
||||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties } from "types";
|
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -32,8 +32,6 @@ type Props = {
|
|||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
||||||
openIssuesListModal: () => void;
|
openIssuesListModal: () => void;
|
||||||
removeIssueFromCycle: (bridgeId: string) => void;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
setPreloadedData: React.Dispatch<
|
setPreloadedData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@ -44,6 +42,7 @@ type Props = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
stateId: string | null;
|
stateId: string | null;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleModuleBoard: React.FC<Props> = ({
|
const SingleModuleBoard: React.FC<Props> = ({
|
||||||
@ -55,18 +54,17 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
bgColor,
|
bgColor,
|
||||||
openCreateIssueModal,
|
openCreateIssueModal,
|
||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromCycle,
|
|
||||||
partialUpdateIssue,
|
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
stateId,
|
stateId,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
// collapse/expand
|
// collapse/expand
|
||||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug, cycleId } = router.query;
|
||||||
|
|
||||||
if (selectedGroup === "priority")
|
if (selectedGroup === "priority")
|
||||||
groupTitle === "high"
|
groupTitle === "high"
|
||||||
@ -132,13 +130,15 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
{...provided.dragHandleProps}
|
{...provided.dragHandleProps}
|
||||||
>
|
>
|
||||||
<SingleIssue
|
<SingleIssue
|
||||||
|
type="cycle"
|
||||||
|
typeId={cycleId as string}
|
||||||
issue={childIssue}
|
issue={childIssue}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
snapshot={snapshot}
|
snapshot={snapshot}
|
||||||
assignees={assignees}
|
assignees={assignees}
|
||||||
people={people}
|
people={people}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -7,14 +7,14 @@ import { mutate } from "swr";
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// headless
|
// headless
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// types
|
|
||||||
import type { ICycle } from "types";
|
|
||||||
// services
|
// services
|
||||||
import cycleService from "services/cycles.service";
|
import cycleService from "services/cycles.service";
|
||||||
import { Button, Input, TextArea, CustomSelect } from "components/ui";
|
|
||||||
// ui
|
// ui
|
||||||
|
import { Button, Input, TextArea, CustomSelect } from "components/ui";
|
||||||
// common
|
// common
|
||||||
import { renderDateFormat } from "helpers/date-time.helper";
|
import { renderDateFormat } from "helpers/date-time.helper";
|
||||||
|
// types
|
||||||
|
import type { ICycle } from "types";
|
||||||
// fetch keys
|
// fetch keys
|
||||||
import { CYCLE_LIST } from "constants/fetch-keys";
|
import { CYCLE_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -86,11 +86,11 @@ const CreateUpdateCycleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, proj
|
|||||||
CYCLE_LIST(projectId),
|
CYCLE_LIST(projectId),
|
||||||
(prevData) => {
|
(prevData) => {
|
||||||
const newData = prevData?.map((item) => {
|
const newData = prevData?.map((item) => {
|
||||||
if (item.id === res.id) {
|
if (item.id === res.id) return res;
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
|
||||||
return newData;
|
return newData;
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -21,7 +21,7 @@ import { CustomMenu, Spinner } from "components/ui";
|
|||||||
// helpers
|
// helpers
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember } from "types";
|
import { IIssue, IWorkspaceMember, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { WORKSPACE_MEMBERS, STATE_LIST } from "constants/fetch-keys";
|
import { WORKSPACE_MEMBERS, STATE_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ type Props = {
|
|||||||
| null
|
| null
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CyclesListView: React.FC<Props> = ({
|
const CyclesListView: React.FC<Props> = ({
|
||||||
@ -46,6 +47,7 @@ const CyclesListView: React.FC<Props> = ({
|
|||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromCycle,
|
removeIssueFromCycle,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
@ -140,6 +142,7 @@ const CyclesListView: React.FC<Props> = ({
|
|||||||
properties={properties}
|
properties={properties}
|
||||||
editIssue={() => openCreateIssueModal(issue, "edit")}
|
editIssue={() => openCreateIssueModal(issue, "edit")}
|
||||||
removeIssue={() => removeIssueFromCycle(issue.bridge ?? "")}
|
removeIssue={() => removeIssueFromCycle(issue.bridge ?? "")}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -21,17 +21,17 @@ import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deleti
|
|||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import type { IState, IIssue, IssueResponse } from "types";
|
import type { IState, IIssue, IssueResponse, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { STATE_LIST, PROJECT_ISSUES_LIST, PROJECT_MEMBERS } from "constants/fetch-keys";
|
import { STATE_LIST, PROJECT_ISSUES_LIST, PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issues: IIssue[];
|
issues: IIssue[];
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, partialUpdateIssue }) => {
|
const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) => {
|
||||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||||
const [isIssueDeletionOpen, setIsIssueDeletionOpen] = useState(false);
|
const [isIssueDeletionOpen, setIsIssueDeletionOpen] = useState(false);
|
||||||
const [issueDeletionData, setIssueDeletionData] = useState<IIssue | undefined>();
|
const [issueDeletionData, setIssueDeletionData] = useState<IIssue | undefined>();
|
||||||
@ -238,7 +238,7 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, partialUpdateIs
|
|||||||
: "#000000"
|
: "#000000"
|
||||||
}
|
}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,7 +15,7 @@ import { PlusIcon } from "@heroicons/react/24/outline";
|
|||||||
// services
|
// services
|
||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// types
|
// types
|
||||||
import { IIssue, Properties, NestedKeyOf, IWorkspaceMember } from "types";
|
import { IIssue, Properties, NestedKeyOf, IWorkspaceMember, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ type Props = {
|
|||||||
stateId: string | null;
|
stateId: string | null;
|
||||||
createdBy: string | null;
|
createdBy: string | null;
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, childIssueId: string) => void;
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleBoard: React.FC<Props> = ({
|
const SingleBoard: React.FC<Props> = ({
|
||||||
@ -55,7 +55,7 @@ const SingleBoard: React.FC<Props> = ({
|
|||||||
stateId,
|
stateId,
|
||||||
createdBy,
|
createdBy,
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
partialUpdateIssue,
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
// Collapse/Expand
|
// Collapse/Expand
|
||||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||||
@ -145,7 +145,7 @@ const SingleBoard: React.FC<Props> = ({
|
|||||||
people={people}
|
people={people}
|
||||||
assignees={assignees}
|
assignees={assignees}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -20,7 +20,7 @@ import SingleListIssue from "components/common/list-view/single-issue";
|
|||||||
// helpers
|
// helpers
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember } from "types";
|
import { IIssue, IWorkspaceMember, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -28,10 +28,10 @@ import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
|||||||
type Props = {
|
type Props = {
|
||||||
issues: IIssue[];
|
issues: IIssue[];
|
||||||
handleEditIssue: (issue: IIssue) => void;
|
handleEditIssue: (issue: IIssue) => void;
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ListView: React.FC<Props> = ({ issues, handleEditIssue }) => {
|
const ListView: React.FC<Props> = ({ issues, handleEditIssue, userAuth }) => {
|
||||||
const [isCreateIssuesModalOpen, setIsCreateIssuesModalOpen] = useState(false);
|
const [isCreateIssuesModalOpen, setIsCreateIssuesModalOpen] = useState(false);
|
||||||
const [preloadedData, setPreloadedData] = useState<
|
const [preloadedData, setPreloadedData] = useState<
|
||||||
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
(Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
||||||
@ -130,6 +130,7 @@ const ListView: React.FC<Props> = ({ issues, handleEditIssue }) => {
|
|||||||
issue={issue}
|
issue={issue}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
editIssue={() => handleEditIssue(issue)}
|
editIssue={() => handleEditIssue(issue)}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -17,7 +17,7 @@ import SingleBoard from "components/project/modules/board-view/single-board";
|
|||||||
// ui
|
// ui
|
||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IProjectMember, ModuleIssueResponse } from "types";
|
import { IIssue, IProjectMember, ModuleIssueResponse, UserAuth } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys";
|
import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -26,8 +26,6 @@ type Props = {
|
|||||||
members: IProjectMember[] | undefined;
|
members: IProjectMember[] | undefined;
|
||||||
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
||||||
openIssuesListModal: () => void;
|
openIssuesListModal: () => void;
|
||||||
removeIssueFromModule: (issueId: string) => void;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
setPreloadedData: React.Dispatch<
|
setPreloadedData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@ -37,6 +35,7 @@ type Props = {
|
|||||||
| null
|
| null
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModulesBoardView: React.FC<Props> = ({
|
const ModulesBoardView: React.FC<Props> = ({
|
||||||
@ -44,10 +43,9 @@ const ModulesBoardView: React.FC<Props> = ({
|
|||||||
members,
|
members,
|
||||||
openCreateIssueModal,
|
openCreateIssueModal,
|
||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromModule,
|
|
||||||
partialUpdateIssue,
|
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
@ -154,10 +152,8 @@ const ModulesBoardView: React.FC<Props> = ({
|
|||||||
: "#000000"
|
: "#000000"
|
||||||
}
|
}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
removeIssueFromModule={removeIssueFromModule}
|
|
||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
openCreateIssueModal={openCreateIssueModal}
|
openCreateIssueModal={openCreateIssueModal}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
stateId={
|
stateId={
|
||||||
@ -165,6 +161,7 @@ const ModulesBoardView: React.FC<Props> = ({
|
|||||||
? states?.find((s) => s.name === singleGroup)?.id ?? null
|
? states?.find((s) => s.name === singleGroup)?.id ?? null
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@ import { CustomMenu } from "components/ui";
|
|||||||
// icons
|
// icons
|
||||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties } from "types";
|
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -32,8 +32,6 @@ type Props = {
|
|||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
|
||||||
openIssuesListModal: () => void;
|
openIssuesListModal: () => void;
|
||||||
removeIssueFromModule: (bridgeId: string) => void;
|
|
||||||
partialUpdateIssue: (formData: Partial<IIssue>, issueId: string) => void;
|
|
||||||
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
setPreloadedData: React.Dispatch<
|
setPreloadedData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@ -44,6 +42,7 @@ type Props = {
|
|||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
stateId: string | null;
|
stateId: string | null;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SingleModuleBoard: React.FC<Props> = ({
|
const SingleModuleBoard: React.FC<Props> = ({
|
||||||
@ -55,17 +54,16 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
bgColor,
|
bgColor,
|
||||||
openCreateIssueModal,
|
openCreateIssueModal,
|
||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromModule,
|
|
||||||
partialUpdateIssue,
|
|
||||||
handleDeleteIssue,
|
handleDeleteIssue,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
stateId,
|
stateId,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
// Collapse/Expand
|
// Collapse/Expand
|
||||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug, moduleId } = router.query;
|
||||||
|
|
||||||
if (selectedGroup === "priority")
|
if (selectedGroup === "priority")
|
||||||
groupTitle === "high"
|
groupTitle === "high"
|
||||||
@ -112,10 +110,10 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
>
|
>
|
||||||
{groupedByIssues[groupTitle].map((childIssue, index: number) => {
|
{groupedByIssues[groupTitle].map((issue, index: number) => {
|
||||||
const assignees = [
|
const assignees = [
|
||||||
...(childIssue?.assignees_list ?? []),
|
...(issue?.assignees_list ?? []),
|
||||||
...(childIssue?.assignees ?? []),
|
...(issue?.assignees ?? []),
|
||||||
]?.map((assignee) => {
|
]?.map((assignee) => {
|
||||||
const tempPerson = people?.find((p) => p.member.id === assignee)?.member;
|
const tempPerson = people?.find((p) => p.member.id === assignee)?.member;
|
||||||
|
|
||||||
@ -123,7 +121,7 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Draggable key={childIssue.id} draggableId={childIssue.id} index={index}>
|
<Draggable key={issue.id} draggableId={issue.id} index={index}>
|
||||||
{(provided, snapshot) => (
|
{(provided, snapshot) => (
|
||||||
<div
|
<div
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
@ -131,13 +129,15 @@ const SingleModuleBoard: React.FC<Props> = ({
|
|||||||
{...provided.dragHandleProps}
|
{...provided.dragHandleProps}
|
||||||
>
|
>
|
||||||
<SingleIssue
|
<SingleIssue
|
||||||
issue={childIssue}
|
type="module"
|
||||||
|
typeId={moduleId as string}
|
||||||
|
issue={issue}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
snapshot={snapshot}
|
snapshot={snapshot}
|
||||||
assignees={assignees}
|
assignees={assignees}
|
||||||
people={people}
|
people={people}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -18,7 +18,7 @@ import { CustomMenu, Spinner } from "components/ui";
|
|||||||
// helpers
|
// helpers
|
||||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IWorkspaceMember } from "types";
|
import { IIssue, IWorkspaceMember, UserAuth } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ type Props = {
|
|||||||
| null
|
| null
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
userAuth: UserAuth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ModulesListView: React.FC<Props> = ({
|
const ModulesListView: React.FC<Props> = ({
|
||||||
@ -43,6 +44,7 @@ const ModulesListView: React.FC<Props> = ({
|
|||||||
openIssuesListModal,
|
openIssuesListModal,
|
||||||
removeIssueFromModule,
|
removeIssueFromModule,
|
||||||
setPreloadedData,
|
setPreloadedData,
|
||||||
|
userAuth,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
@ -137,6 +139,7 @@ const ModulesListView: React.FC<Props> = ({
|
|||||||
properties={properties}
|
properties={properties}
|
||||||
editIssue={() => openCreateIssueModal(issue, "edit")}
|
editIssue={() => openCreateIssueModal(issue, "edit")}
|
||||||
removeIssue={() => removeIssueFromModule(issue.bridge ?? "")}
|
removeIssue={() => removeIssueFromModule(issue.bridge ?? "")}
|
||||||
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,7 @@ import { useForm, Controller } from "react-hook-form";
|
|||||||
import { Dialog, Transition, Listbox } from "@headlessui/react";
|
import { Dialog, Transition, Listbox } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { ChevronDownIcon, CheckIcon } from "@heroicons/react/20/solid";
|
import { ChevronDownIcon, CheckIcon } from "@heroicons/react/20/solid";
|
||||||
import { Button, Select, TextArea } from "components/ui";
|
import { Button, CustomSelect, Select, TextArea } from "components/ui";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// services
|
// services
|
||||||
@ -106,7 +106,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog as="div" className="relative z-10" onClose={handleClose}>
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
@ -119,7 +119,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
@ -196,40 +196,17 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
|
|||||||
uninvitedPeople?.map((person) => (
|
uninvitedPeople?.map((person) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={person.member.id}
|
key={person.member.id}
|
||||||
className={({ active }) =>
|
className={({ active, selected }) =>
|
||||||
`${
|
`${active ? "bg-indigo-50" : ""} ${
|
||||||
active ? "bg-theme text-white" : "text-gray-900"
|
selected ? "bg-indigo-50 font-medium" : ""
|
||||||
} relative cursor-default select-none py-2 pl-3 pr-9 text-left`
|
} text-gray-900 cursor-default select-none p-2`
|
||||||
}
|
}
|
||||||
value={{
|
value={{
|
||||||
id: person.member.id,
|
id: person.member.id,
|
||||||
email: person.member.email,
|
email: person.member.email,
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
{({ selected, active }) => (
|
|
||||||
<>
|
|
||||||
<span
|
|
||||||
className={`${
|
|
||||||
selected ? "font-semibold" : "font-normal"
|
|
||||||
} block truncate`}
|
|
||||||
>
|
>
|
||||||
{person.member.email}
|
{person.member.email}
|
||||||
</span>
|
|
||||||
|
|
||||||
{selected ? (
|
|
||||||
<span
|
|
||||||
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
|
|
||||||
active ? "text-white" : "text-theme"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<CheckIcon
|
|
||||||
className="h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Listbox.Option>
|
</Listbox.Option>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
@ -246,23 +223,29 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<h6 className="text-gray-500">Role</h6>
|
||||||
<Select
|
<Controller
|
||||||
id="role"
|
|
||||||
label="Role"
|
|
||||||
name="role"
|
name="role"
|
||||||
error={errors.role}
|
control={control}
|
||||||
register={register}
|
render={({ field }) => (
|
||||||
validations={{
|
<CustomSelect
|
||||||
required: "Role is required",
|
{...field}
|
||||||
}}
|
label={
|
||||||
options={Object.entries(ROLE).map(([key, value]) => ({
|
<span className="capitalize">
|
||||||
value: key,
|
{field.value ? ROLE[field.value] : "Select role"}
|
||||||
label: value,
|
</span>
|
||||||
}))}
|
}
|
||||||
|
input
|
||||||
|
>
|
||||||
|
{Object.entries(ROLE).map(([key, label]) => (
|
||||||
|
<CustomSelect.Option key={key} value={key}>
|
||||||
|
{label}
|
||||||
|
</CustomSelect.Option>
|
||||||
|
))}
|
||||||
|
</CustomSelect>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<TextArea
|
<TextArea
|
||||||
id="message"
|
id="message"
|
||||||
|
@ -14,6 +14,7 @@ type CustomSelectProps = {
|
|||||||
width?: "auto" | string;
|
width?: "auto" | string;
|
||||||
input?: boolean;
|
input?: boolean;
|
||||||
noChevron?: boolean;
|
noChevron?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomSelect = ({
|
const CustomSelect = ({
|
||||||
@ -26,11 +27,20 @@ const CustomSelect = ({
|
|||||||
width = "auto",
|
width = "auto",
|
||||||
input = false,
|
input = false,
|
||||||
noChevron = false,
|
noChevron = false,
|
||||||
|
disabled = false,
|
||||||
}: CustomSelectProps) => (
|
}: CustomSelectProps) => (
|
||||||
<Listbox as="div" value={value} onChange={onChange} className="relative flex-shrink-0 text-left">
|
<Listbox
|
||||||
|
as="div"
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
className="relative flex-shrink-0 text-left"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
className={`flex w-full cursor-pointer items-center justify-between gap-1 rounded-md border shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
className={`flex w-full ${
|
||||||
|
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||||
|
} items-center justify-between gap-1 rounded-md border shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
||||||
input ? "border-gray-300 px-3 py-2 text-sm" : "px-2 py-1 text-xs"
|
input ? "border-gray-300 px-3 py-2 text-sm" : "px-2 py-1 text-xs"
|
||||||
} ${
|
} ${
|
||||||
textAlignment === "right"
|
textAlignment === "right"
|
||||||
|
@ -4,19 +4,20 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
|
// headless ui
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
// constants
|
|
||||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
|
||||||
import type { IWorkspace } from "types";
|
|
||||||
import { USER_WORKSPACES } from "constants/fetch-keys";
|
|
||||||
// services
|
// services
|
||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// icons
|
// icons
|
||||||
|
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "components/ui";
|
import { Button, Input } from "components/ui";
|
||||||
// types
|
// types
|
||||||
|
import type { IWorkspace } from "types";
|
||||||
|
// fetch-keys
|
||||||
|
import { USER_WORKSPACES } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -78,7 +79,7 @@ const ConfirmWorkspaceDeletion: React.FC<Props> = ({ isOpen, data, onClose }) =>
|
|||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10"
|
className="relative z-20"
|
||||||
initialFocus={cancelButtonRef}
|
initialFocus={cancelButtonRef}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
@ -94,7 +95,7 @@ const ConfirmWorkspaceDeletion: React.FC<Props> = ({ isOpen, data, onClose }) =>
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
|
@ -33,7 +33,7 @@ const ConfirmWorkspaceMemberRemove: React.FC<Props> = ({ isOpen, onClose, data,
|
|||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog
|
<Dialog
|
||||||
as="div"
|
as="div"
|
||||||
className="relative z-10"
|
className="relative z-20"
|
||||||
initialFocus={cancelButtonRef}
|
initialFocus={cancelButtonRef}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
>
|
>
|
||||||
@ -49,7 +49,7 @@ const ConfirmWorkspaceMemberRemove: React.FC<Props> = ({ isOpen, onClose, data,
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
|
@ -79,7 +79,7 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||||
<Dialog as="div" className="relative z-10" onClose={handleClose}>
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
enter="ease-out duration-300"
|
enter="ease-out duration-300"
|
||||||
@ -92,7 +92,7 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
|
|||||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||||
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
||||||
<Transition.Child
|
<Transition.Child
|
||||||
as={React.Fragment}
|
as={React.Fragment}
|
||||||
|
@ -4,6 +4,8 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
|
// lib
|
||||||
|
import { requiredAdmin, requiredAuth } from "lib/auth";
|
||||||
// layouts
|
// layouts
|
||||||
import AppLayout from "layouts/app-layout";
|
import AppLayout from "layouts/app-layout";
|
||||||
// contexts
|
// contexts
|
||||||
@ -32,7 +34,8 @@ import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
|
|||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
// icons
|
// icons
|
||||||
// types
|
// types
|
||||||
import { CycleIssueResponse, IIssue, SelectIssue } from "types";
|
import { CycleIssueResponse, IIssue, SelectIssue, UserAuth } from "types";
|
||||||
|
import { NextPageContext } from "next";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import {
|
import {
|
||||||
CYCLE_ISSUES,
|
CYCLE_ISSUES,
|
||||||
@ -42,7 +45,7 @@ import {
|
|||||||
PROJECT_DETAILS,
|
PROJECT_DETAILS,
|
||||||
} from "constants/fetch-keys";
|
} from "constants/fetch-keys";
|
||||||
|
|
||||||
const SingleCycle: React.FC = () => {
|
const SingleCycle: React.FC<UserAuth> = (props) => {
|
||||||
const [isIssueModalOpen, setIsIssueModalOpen] = useState(false);
|
const [isIssueModalOpen, setIsIssueModalOpen] = useState(false);
|
||||||
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
||||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||||
@ -109,18 +112,6 @@ const SingleCycle: React.FC = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const partialUpdateIssue = (formData: Partial<IIssue>, issueId: string) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
issuesServices
|
|
||||||
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
|
|
||||||
.then(() => {
|
|
||||||
mutate(CYCLE_ISSUES(cycleId as string));
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openCreateIssueModal = (
|
const openCreateIssueModal = (
|
||||||
issue?: IIssue,
|
issue?: IIssue,
|
||||||
actionType: "create" | "edit" | "delete" = "create"
|
actionType: "create" | "edit" | "delete" = "create"
|
||||||
@ -256,16 +247,16 @@ const SingleCycle: React.FC = () => {
|
|||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
removeIssueFromCycle={removeIssueFromCycle}
|
removeIssueFromCycle={removeIssueFromCycle}
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
<CyclesBoardView
|
<CyclesBoardView
|
||||||
issues={cycleIssuesArray ?? []}
|
issues={cycleIssuesArray ?? []}
|
||||||
removeIssueFromCycle={removeIssueFromCycle}
|
|
||||||
members={members}
|
members={members}
|
||||||
openCreateIssueModal={openCreateIssueModal}
|
openCreateIssueModal={openCreateIssueModal}
|
||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
handleDeleteIssue={setDeleteIssue}
|
handleDeleteIssue={setDeleteIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -309,4 +300,32 @@ const SingleCycle: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps = async (ctx: NextPageContext) => {
|
||||||
|
const user = await requiredAuth(ctx.req?.headers.cookie);
|
||||||
|
const redirectAfterSignIn = ctx.req?.url;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: `/signin?next=${redirectAfterSignIn}`,
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectId = ctx.query.projectId as string;
|
||||||
|
const workspaceSlug = ctx.query.workspaceSlug as string;
|
||||||
|
|
||||||
|
const memberDetail = await requiredAdmin(workspaceSlug, projectId, ctx.req?.headers.cookie);
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isOwner: memberDetail?.role === 20,
|
||||||
|
isMember: memberDetail?.role === 15,
|
||||||
|
isViewer: memberDetail?.role === 10,
|
||||||
|
isGuest: memberDetail?.role === 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default SingleCycle;
|
export default SingleCycle;
|
||||||
|
@ -4,7 +4,7 @@ import useSWR, { mutate } from "swr";
|
|||||||
import { RectangleStackIcon } from "@heroicons/react/24/outline";
|
import { RectangleStackIcon } from "@heroicons/react/24/outline";
|
||||||
import { PlusIcon } from "@heroicons/react/20/solid";
|
import { PlusIcon } from "@heroicons/react/20/solid";
|
||||||
// lib
|
// lib
|
||||||
import { requiredAuth } from "lib/auth";
|
import { requiredAdmin, requiredAuth } from "lib/auth";
|
||||||
// services
|
// services
|
||||||
import issuesServices from "services/issues.service";
|
import issuesServices from "services/issues.service";
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
@ -22,12 +22,12 @@ import View from "components/core/view";
|
|||||||
import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton } from "components/ui";
|
import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton } from "components/ui";
|
||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
// types
|
// types
|
||||||
import type { IIssue, IssueResponse } from "types";
|
import type { IIssue, IssueResponse, UserAuth } from "types";
|
||||||
import type { NextPage, NextPageContext } from "next";
|
import type { NextPage, NextPageContext } from "next";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
import { PROJECT_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
|
|
||||||
const ProjectIssues: NextPage = () => {
|
const ProjectIssues: NextPage<UserAuth> = (props) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [selectedIssue, setSelectedIssue] = useState<
|
const [selectedIssue, setSelectedIssue] = useState<
|
||||||
(IIssue & { actionType: "edit" | "delete" }) | undefined
|
(IIssue & { actionType: "edit" | "delete" }) | undefined
|
||||||
@ -63,26 +63,6 @@ const ProjectIssues: NextPage = () => {
|
|||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
const partialUpdateIssue = (formData: Partial<IIssue>, issueId: string) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
issuesServices
|
|
||||||
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
|
|
||||||
.then((response) => {
|
|
||||||
mutate<IssueResponse>(
|
|
||||||
PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string),
|
|
||||||
(prevData) => ({
|
|
||||||
...(prevData as IssueResponse),
|
|
||||||
results:
|
|
||||||
prevData?.results.map((issue) => (issue.id === response.id ? response : issue)) ?? [],
|
|
||||||
}),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditIssue = (issue: IIssue) => {
|
const handleEditIssue = (issue: IIssue) => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
setSelectedIssue({ ...issue, actionType: "edit" });
|
setSelectedIssue({ ...issue, actionType: "edit" });
|
||||||
@ -134,12 +114,12 @@ const ProjectIssues: NextPage = () => {
|
|||||||
<ListView
|
<ListView
|
||||||
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
||||||
handleEditIssue={handleEditIssue}
|
handleEditIssue={handleEditIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
<BoardView
|
<BoardView
|
||||||
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
|
||||||
handleDeleteIssue={setDeleteIssue}
|
handleDeleteIssue={setDeleteIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@ -170,7 +150,6 @@ const ProjectIssues: NextPage = () => {
|
|||||||
|
|
||||||
export const getServerSideProps = async (ctx: NextPageContext) => {
|
export const getServerSideProps = async (ctx: NextPageContext) => {
|
||||||
const user = await requiredAuth(ctx.req?.headers.cookie);
|
const user = await requiredAuth(ctx.req?.headers.cookie);
|
||||||
|
|
||||||
const redirectAfterSignIn = ctx.req?.url;
|
const redirectAfterSignIn = ctx.req?.url;
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
@ -182,9 +161,17 @@ export const getServerSideProps = async (ctx: NextPageContext) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const projectId = ctx.query.projectId as string;
|
||||||
|
const workspaceSlug = ctx.query.workspaceSlug as string;
|
||||||
|
|
||||||
|
const memberDetail = await requiredAdmin(workspaceSlug, projectId, ctx.req?.headers.cookie);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
user,
|
isOwner: memberDetail?.role === 20,
|
||||||
|
isMember: memberDetail?.role === 15,
|
||||||
|
isViewer: memberDetail?.role === 10,
|
||||||
|
isGuest: memberDetail?.role === 5,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,9 @@ import React, { useState } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
|
// lib
|
||||||
|
import { requiredAdmin, requiredAuth } from "lib/auth";
|
||||||
// services
|
// services
|
||||||
import modulesService from "services/modules.service";
|
import modulesService from "services/modules.service";
|
||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
@ -32,7 +35,15 @@ import {
|
|||||||
RectangleStackIcon,
|
RectangleStackIcon,
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IModule, ModuleIssueResponse, SelectIssue, SelectModuleType } from "types";
|
import {
|
||||||
|
IIssue,
|
||||||
|
IModule,
|
||||||
|
ModuleIssueResponse,
|
||||||
|
SelectIssue,
|
||||||
|
SelectModuleType,
|
||||||
|
UserAuth,
|
||||||
|
} from "types";
|
||||||
|
import { NextPageContext } from "next";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import {
|
import {
|
||||||
MODULE_DETAIL,
|
MODULE_DETAIL,
|
||||||
@ -42,7 +53,7 @@ import {
|
|||||||
PROJECT_MEMBERS,
|
PROJECT_MEMBERS,
|
||||||
} from "constants/fetch-keys";
|
} from "constants/fetch-keys";
|
||||||
|
|
||||||
const SingleModule = () => {
|
const SingleModule: React.FC<UserAuth> = (props) => {
|
||||||
const [moduleSidebar, setModuleSidebar] = useState(true);
|
const [moduleSidebar, setModuleSidebar] = useState(true);
|
||||||
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
|
||||||
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>(null);
|
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>(null);
|
||||||
@ -128,18 +139,6 @@ const SingleModule = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const partialUpdateIssue = (formData: Partial<IIssue>, issueId: string) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
issuesService
|
|
||||||
.patchIssue(workspaceSlug as string, projectId as string, issueId, formData)
|
|
||||||
.then(() => {
|
|
||||||
mutate(MODULE_ISSUES(moduleId as string));
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openCreateIssueModal = (
|
const openCreateIssueModal = (
|
||||||
issue?: IIssue,
|
issue?: IIssue,
|
||||||
actionType: "create" | "edit" | "delete" = "create"
|
actionType: "create" | "edit" | "delete" = "create"
|
||||||
@ -279,16 +278,16 @@ const SingleModule = () => {
|
|||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
removeIssueFromModule={removeIssueFromModule}
|
removeIssueFromModule={removeIssueFromModule}
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
<ModulesBoardView
|
<ModulesBoardView
|
||||||
issues={moduleIssuesArray ?? []}
|
issues={moduleIssuesArray ?? []}
|
||||||
removeIssueFromModule={removeIssueFromModule}
|
|
||||||
members={members}
|
members={members}
|
||||||
openCreateIssueModal={openCreateIssueModal}
|
openCreateIssueModal={openCreateIssueModal}
|
||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
handleDeleteIssue={setDeleteIssue}
|
handleDeleteIssue={setDeleteIssue}
|
||||||
partialUpdateIssue={partialUpdateIssue}
|
|
||||||
setPreloadedData={setPreloadedData}
|
setPreloadedData={setPreloadedData}
|
||||||
|
userAuth={props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -333,4 +332,32 @@ const SingleModule = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getServerSideProps = async (ctx: NextPageContext) => {
|
||||||
|
const user = await requiredAuth(ctx.req?.headers.cookie);
|
||||||
|
const redirectAfterSignIn = ctx.req?.url;
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: `/signin?next=${redirectAfterSignIn}`,
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectId = ctx.query.projectId as string;
|
||||||
|
const workspaceSlug = ctx.query.workspaceSlug as string;
|
||||||
|
|
||||||
|
const memberDetail = await requiredAdmin(workspaceSlug, projectId, ctx.req?.headers.cookie);
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isOwner: memberDetail?.role === 20,
|
||||||
|
isMember: memberDetail?.role === 15,
|
||||||
|
isViewer: memberDetail?.role === 10,
|
||||||
|
isGuest: memberDetail?.role === 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default SingleModule;
|
export default SingleModule;
|
||||||
|
@ -6,7 +6,6 @@ import Image from "next/image";
|
|||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import type { NextPageContext, NextPage } from "next";
|
|
||||||
// lib
|
// lib
|
||||||
import { requiredAdmin } from "lib/auth";
|
import { requiredAdmin } from "lib/auth";
|
||||||
// layouts
|
// layouts
|
||||||
@ -21,6 +20,7 @@ import { Button, CustomSelect, Loader } from "components/ui";
|
|||||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||||
// types
|
// types
|
||||||
import { IProject, IWorkspace } from "types";
|
import { IProject, IWorkspace } from "types";
|
||||||
|
import type { NextPageContext, NextPage } from "next";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECTS_LIST, PROJECT_DETAILS, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
import { PROJECTS_LIST, PROJECT_DETAILS, WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -88,24 +88,9 @@ const ControlSettings: NextPage<TControlSettingsProps> = (props) => {
|
|||||||
await projectService
|
await projectService
|
||||||
.updateProject(workspaceSlug as string, projectId as string, payload)
|
.updateProject(workspaceSlug as string, projectId as string, payload)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
mutate<IProject>(
|
mutate(PROJECT_DETAILS(projectId as string));
|
||||||
PROJECT_DETAILS(projectId as string),
|
mutate(PROJECTS_LIST(workspaceSlug as string));
|
||||||
(prevData) => ({ ...prevData, ...res }),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
mutate<IProject[]>(
|
|
||||||
PROJECTS_LIST(workspaceSlug as string),
|
|
||||||
(prevData) => {
|
|
||||||
const newData = prevData?.map((item) => {
|
|
||||||
if (item.id === res.id) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
return newData;
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
type: "success",
|
type: "success",
|
||||||
|
@ -5,9 +5,6 @@ import useSWR from "swr";
|
|||||||
|
|
||||||
// icons
|
// icons
|
||||||
import { CubeIcon, PlusIcon } from "@heroicons/react/24/outline";
|
import { CubeIcon, PlusIcon } from "@heroicons/react/24/outline";
|
||||||
// types
|
|
||||||
import type { NextPage, NextPageContext } from "next";
|
|
||||||
import type { IWorkspaceMemberInvitation } from "types";
|
|
||||||
// services
|
// services
|
||||||
import workspaceService from "services/workspace.service";
|
import workspaceService from "services/workspace.service";
|
||||||
// hooks
|
// hooks
|
||||||
@ -16,9 +13,12 @@ import { requiredAuth } from "lib/auth";
|
|||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// components
|
// components
|
||||||
import SingleInvitation from "components/workspace/SingleInvitation";
|
import SingleInvitation from "components/workspace/single-invitation";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Spinner, EmptySpace, EmptySpaceItem } from "components/ui";
|
import { Button, Spinner, EmptySpace, EmptySpaceItem } from "components/ui";
|
||||||
|
// types
|
||||||
|
import type { NextPage, NextPageContext } from "next";
|
||||||
|
import type { IWorkspaceMemberInvitation } from "types";
|
||||||
|
|
||||||
const OnBoard: NextPage = () => {
|
const OnBoard: NextPage = () => {
|
||||||
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
|
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
|
||||||
|
7
apps/app/types/users.d.ts
vendored
7
apps/app/types/users.d.ts
vendored
@ -34,3 +34,10 @@ export interface IUserLite {
|
|||||||
avatar: string;
|
avatar: string;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UserAuth = {
|
||||||
|
isMember: boolean;
|
||||||
|
isOwner: boolean;
|
||||||
|
isViewer: boolean;
|
||||||
|
isGuest: boolean;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user