forked from github/plane
fix: views issues mutation, sidebar link highlight (#1025)
* fix: views issues mutation, sidebar link highlight * fix: show only specific states when type filter is set * fix: delete comment mutation * style: bulk delete issues modal * fix: project settings features mutation
This commit is contained in:
parent
4884ecd668
commit
df96d40cfa
@ -167,7 +167,7 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
Add Issue
|
||||
</button>
|
||||
}
|
||||
optionsPosition="left"
|
||||
position="left"
|
||||
noBorder
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={addIssueToState}>
|
||||
|
@ -121,7 +121,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-brand-surface-2 shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
|
||||
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 rounded-xl border border-brand-base bg-brand-base shadow-2xl transition-all">
|
||||
<form>
|
||||
<Combobox
|
||||
onChange={(val: string) => {
|
||||
@ -149,7 +149,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
||||
|
||||
<Combobox.Options
|
||||
static
|
||||
className="max-h-80 scroll-py-2 divide-y divide-gray-500 divide-opacity-10 overflow-y-auto"
|
||||
className="max-h-80 scroll-py-2 divide-y divide-brand-base overflow-y-auto"
|
||||
>
|
||||
{filteredIssues.length > 0 ? (
|
||||
<li className="p-2">
|
||||
@ -158,15 +158,15 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
||||
Select issues to delete
|
||||
</h2>
|
||||
)}
|
||||
<ul className="text-sm text-gray-700">
|
||||
<ul className="text-sm text-brand-secondary">
|
||||
{filteredIssues.map((issue) => (
|
||||
<Combobox.Option
|
||||
key={issue.id}
|
||||
as="div"
|
||||
value={issue.id}
|
||||
className={({ active }) =>
|
||||
className={({ active, selected }) =>
|
||||
`flex cursor-pointer select-none items-center justify-between rounded-md px-3 py-2 ${
|
||||
active ? "bg-gray-900 bg-opacity-5 text-brand-base" : ""
|
||||
active ? "bg-brand-surface-2 text-brand-base" : ""
|
||||
}`
|
||||
}
|
||||
>
|
||||
@ -182,7 +182,7 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) =>
|
||||
backgroundColor: issue.state_detail.color,
|
||||
}}
|
||||
/>
|
||||
<span className="flex-shrink-0 text-xs text-brand-secondary">
|
||||
<span className="flex-shrink-0 text-xs">
|
||||
{issue.project_detail.identifier}-{issue.sequence_id}
|
||||
</span>
|
||||
<span>{issue.name}</span>
|
||||
|
@ -110,7 +110,7 @@ export const ImageUploadModal: React.FC<Props> = ({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-[#131313] bg-opacity-50 transition-opacity" />
|
||||
<div className="fixed inset-0 bg-brand-backdrop bg-opacity-50 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-30 overflow-y-auto">
|
||||
@ -124,7 +124,7 @@ export const ImageUploadModal: React.FC<Props> = ({
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-brand-surface-2 px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-brand-base bg-brand-base px-5 py-8 text-left shadow-xl transition-all sm:w-full sm:max-w-xl sm:p-6">
|
||||
<div className="space-y-5">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-brand-base">
|
||||
Upload Image
|
||||
@ -133,9 +133,9 @@ export const ImageUploadModal: React.FC<Props> = ({
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
{...getRootProps()}
|
||||
className={`relative block h-80 w-full rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 ${
|
||||
className={`relative grid h-80 w-full cursor-pointer place-items-center rounded-lg p-12 text-center focus:outline-none focus:ring-2 focus:ring-brand-accent focus:ring-offset-2 ${
|
||||
(image === null && isDragActive) || !value
|
||||
? "border-2 border-dashed border-brand-base hover:border-gray-400"
|
||||
? "border-2 border-dashed border-brand-base hover:bg-brand-surface-1"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
@ -143,7 +143,7 @@ export const ImageUploadModal: React.FC<Props> = ({
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-brand-surface-1 px-2 py-0.5 text-xs font-medium text-gray-600"
|
||||
className="absolute top-0 right-0 z-40 translate-x-1/2 -translate-y-1/2 rounded bg-brand-surface-1 px-2 py-0.5 text-xs font-medium text-brand-secondary"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
@ -152,17 +152,18 @@ export const ImageUploadModal: React.FC<Props> = ({
|
||||
objectFit="cover"
|
||||
src={image ? URL.createObjectURL(image) : value ? value : ""}
|
||||
alt="image"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<UserCircleIcon className="mx-auto h-16 w-16 text-gray-400" />
|
||||
<span className="mt-2 block text-sm font-medium text-brand-base">
|
||||
<div>
|
||||
<UserCircleIcon className="mx-auto h-16 w-16 text-brand-secondary" />
|
||||
<span className="mt-2 block text-sm font-medium text-brand-secondary">
|
||||
{isDragActive
|
||||
? "Drop image here to upload"
|
||||
: "Drag & drop image here"}
|
||||
</span>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input {...getInputProps()} type="text" />
|
||||
|
@ -3,12 +3,12 @@ export * from "./list-view";
|
||||
export * from "./sidebar";
|
||||
export * from "./bulk-delete-issues-modal";
|
||||
export * from "./existing-issues-list-modal";
|
||||
export * from "./filters-list";
|
||||
export * from "./gpt-assistant-modal";
|
||||
export * from "./image-upload-modal";
|
||||
export * from "./issues-view-filter";
|
||||
export * from "./issues-view";
|
||||
export * from "./link-modal";
|
||||
export * from "./image-picker-popover";
|
||||
export * from "./filter-list";
|
||||
export * from "./feeds";
|
||||
export * from "./theme-switch";
|
||||
|
@ -168,7 +168,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</div>
|
||||
}
|
||||
optionsPosition="right"
|
||||
position="right"
|
||||
noBorder
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={addIssueToState}>Create new</CustomMenu.MenuItem>
|
||||
@ -204,7 +204,8 @@ export const SingleList: React.FC<Props> = ({
|
||||
makeIssueCopy={() => makeIssueCopy(issue)}
|
||||
handleDeleteIssue={handleDeleteIssue}
|
||||
removeIssue={() => {
|
||||
if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id, issue.id);
|
||||
if (removeIssue !== null && issue.bridge_id)
|
||||
removeIssue(issue.bridge_id, issue.id);
|
||||
}}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
|
@ -143,6 +143,7 @@ export const JiraGetImportDetail: React.FC = () => {
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
verticalPosition="top"
|
||||
>
|
||||
{projects.length > 0 ? (
|
||||
projects.map((project) => (
|
||||
|
@ -152,6 +152,9 @@ export const IssueActivitySection: React.FC = () => {
|
||||
|
||||
const handleCommentDelete = async (commentId: string) => {
|
||||
if (!workspaceSlug || !projectId || !issueId) return;
|
||||
|
||||
mutateIssueActivities((prevData) => prevData?.filter((p) => p.id !== commentId), false);
|
||||
|
||||
await issuesService
|
||||
.deleteIssueComment(
|
||||
workspaceSlug as string,
|
||||
|
@ -120,11 +120,11 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
const handleCycleChange = useCallback(
|
||||
(cycleDetail: ICycle) => {
|
||||
(cycleDetails: ICycle) => {
|
||||
if (!workspaceSlug || !projectId || !issueDetail) return;
|
||||
|
||||
issuesService
|
||||
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetail.id, {
|
||||
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetails.id, {
|
||||
issues: [issueDetail.id],
|
||||
})
|
||||
.then((res) => {
|
||||
@ -361,6 +361,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
||||
target_date: val,
|
||||
})
|
||||
}
|
||||
className="bg-brand-surface-1"
|
||||
disabled={isNotAllowed}
|
||||
/>
|
||||
)}
|
||||
|
@ -322,7 +322,7 @@ export const SubIssuesList: FC<Props> = ({ parentIssue }) => {
|
||||
Add sub-issue
|
||||
</>
|
||||
}
|
||||
optionsPosition="left"
|
||||
position="left"
|
||||
noBorder
|
||||
noChevron
|
||||
>
|
||||
|
@ -322,7 +322,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2 ${
|
||||
className={`flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2 ${
|
||||
iAmFeelingLucky ? "cursor-wait bg-brand-surface-2" : ""
|
||||
}`}
|
||||
onClick={handleAutoGenerateDescription}
|
||||
@ -338,7 +338,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="-mr-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2"
|
||||
className="-mr-2 flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2"
|
||||
onClick={() => setGptAssistantModal((prevData) => !prevData)}
|
||||
>
|
||||
<SparklesIcon className="h-4 w-4" />
|
||||
@ -346,7 +346,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="-mr-2 flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-brand-surface-2"
|
||||
className="-mr-2 flex items-center gap-1 rounded bg-brand-surface-1 px-1.5 py-1 text-xs hover:bg-brand-surface-2"
|
||||
onClick={() => setCreateBlockForm(true)}
|
||||
>
|
||||
<PencilIcon className="h-3.5 w-3.5" />
|
||||
@ -354,7 +354,7 @@ export const SinglePageBlock: React.FC<Props> = ({ block, projectDetails, index
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<button
|
||||
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded px-2.5 py-1 text-left text-xs duration-300 hover:bg-brand-surface-2"
|
||||
className="flex w-full cursor-pointer items-center justify-between gap-1 rounded bg-brand-surface-1 px-2.5 py-1 text-left text-xs duration-300 hover:bg-brand-surface-2"
|
||||
onClick={() => setIsMenuActive(!isMenuActive)}
|
||||
>
|
||||
<BoltIcon className="h-4.5 w-3.5" />
|
||||
|
@ -37,8 +37,8 @@ export const ProjectSidebarList: FC = () => {
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { data: favoriteProjects } = useSWR(
|
||||
workspaceSlug ? FAVORITE_PROJECTS_LIST(workspaceSlug as string) : null,
|
||||
() => (workspaceSlug ? projectService.getFavoriteProjects(workspaceSlug as string) : null)
|
||||
workspaceSlug ? FAVORITE_PROJECTS_LIST(workspaceSlug.toString()) : null,
|
||||
() => (workspaceSlug ? projectService.getFavoriteProjects(workspaceSlug.toString()) : null)
|
||||
);
|
||||
|
||||
const { data: projects } = useSWR(
|
||||
|
@ -17,7 +17,8 @@ type Props = {
|
||||
textAlignment?: "left" | "center" | "right";
|
||||
noBorder?: boolean;
|
||||
noChevron?: boolean;
|
||||
optionsPosition?: "left" | "right";
|
||||
position?: "left" | "right";
|
||||
verticalPosition?: "top" | "bottom";
|
||||
customButton?: JSX.Element;
|
||||
};
|
||||
|
||||
@ -40,7 +41,8 @@ const CustomMenu = ({
|
||||
textAlignment,
|
||||
noBorder = false,
|
||||
noChevron = false,
|
||||
optionsPosition = "right",
|
||||
position = "right",
|
||||
verticalPosition = "bottom",
|
||||
customButton,
|
||||
}: Props) => (
|
||||
<Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}>
|
||||
@ -103,9 +105,9 @@ const CustomMenu = ({
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className={`absolute z-20 mt-1 overflow-y-scroll whitespace-nowrap rounded-md border border-brand-base bg-brand-surface-1 p-1 text-xs shadow-lg focus:outline-none ${
|
||||
optionsPosition === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
||||
} ${
|
||||
className={`absolute z-20 overflow-y-scroll whitespace-nowrap rounded-md border border-brand-base bg-brand-surface-1 p-1 text-xs shadow-lg focus:outline-none ${
|
||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
||||
height === "sm"
|
||||
? "max-h-28"
|
||||
: height === "md"
|
||||
|
@ -15,6 +15,7 @@ type CustomSearchSelectProps = {
|
||||
textAlignment?: "left" | "center" | "right";
|
||||
height?: "sm" | "md" | "rg" | "lg";
|
||||
position?: "right" | "left";
|
||||
verticalPosition?: "top" | "bottom";
|
||||
noChevron?: boolean;
|
||||
customButton?: JSX.Element;
|
||||
optionsClassName?: string;
|
||||
@ -32,6 +33,7 @@ export const CustomSearchSelect = ({
|
||||
onChange,
|
||||
options,
|
||||
position = "left",
|
||||
verticalPosition = "bottom",
|
||||
noChevron = false,
|
||||
customButton,
|
||||
optionsClassName = "",
|
||||
@ -99,7 +101,9 @@ export const CustomSearchSelect = ({
|
||||
<Combobox.Options
|
||||
className={`${optionsClassName} absolute min-w-[10rem] border border-brand-base p-2 ${
|
||||
position === "right" ? "right-0" : "left-0"
|
||||
} z-10 mt-1 origin-top-right rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none`}
|
||||
} ${
|
||||
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
|
||||
} z-10 origin-top-right rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none`}
|
||||
>
|
||||
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-brand-base bg-brand-surface-1 px-2">
|
||||
<MagnifyingGlassIcon className="h-3 w-3 text-brand-secondary" />
|
||||
|
@ -13,6 +13,7 @@ type CustomSelectProps = {
|
||||
textAlignment?: "left" | "center" | "right";
|
||||
maxHeight?: "sm" | "rg" | "md" | "lg" | "none";
|
||||
position?: "right" | "left";
|
||||
verticalPosition?: "top" | "bottom";
|
||||
width?: "auto" | string;
|
||||
input?: boolean;
|
||||
noChevron?: boolean;
|
||||
@ -30,6 +31,7 @@ const CustomSelect = ({
|
||||
onChange,
|
||||
maxHeight = "none",
|
||||
position = "left",
|
||||
verticalPosition = "bottom",
|
||||
width = "auto",
|
||||
input = false,
|
||||
noChevron = false,
|
||||
@ -80,6 +82,8 @@ const CustomSelect = ({
|
||||
<Listbox.Options
|
||||
className={`${optionsClassName} absolute border border-brand-base ${
|
||||
position === "right" ? "right-0" : "left-0"
|
||||
} ${
|
||||
verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"
|
||||
} z-10 mt-1 origin-top-right overflow-y-auto rounded-md bg-brand-surface-1 text-xs shadow-lg focus:outline-none ${
|
||||
width === "auto" ? "min-w-[8rem] whitespace-nowrap" : width
|
||||
} ${input ? "max-h-48" : ""} ${
|
||||
|
@ -18,13 +18,16 @@ const EmptySpace: React.FC<EmptySpaceProps> = ({ title, description, children, I
|
||||
<div className="max-w-lg">
|
||||
{Icon ? (
|
||||
<div className="mb-4">
|
||||
<Icon className="h-14 w-14 text-gray-400" />
|
||||
<Icon className="h-14 w-14 text-brand-secondary" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<h2 className="text-lg font-medium text-brand-base">{title}</h2>
|
||||
<div className="mt-1 text-sm text-brand-secondary">{description}</div>
|
||||
<ul role="list" className="mt-6 divide-y divide-gray-200 border-t border-brand-base border-b">
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-6 divide-y divide-brand-base border-t border-b border-brand-base"
|
||||
>
|
||||
{children}
|
||||
</ul>
|
||||
{link ? (
|
||||
@ -57,7 +60,7 @@ const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Ico
|
||||
} space-x-3 py-4`}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<span className={`inline-flex h-10 w-10 items-center justify-center rounded-lg bg-brand-accent`}>
|
||||
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-brand-accent">
|
||||
<Icon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
@ -67,7 +70,7 @@ const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Ico
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRightIcon
|
||||
className="h-5 w-5 text-gray-400 group-hover:text-brand-secondary"
|
||||
className="h-5 w-5 text-brand-base group-hover:text-brand-secondary"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
@ -43,9 +43,9 @@ export const WorkspaceSidebarMenu: React.FC = () => {
|
||||
<a
|
||||
className={`${
|
||||
(
|
||||
link.name === "Dashboard"
|
||||
? router.asPath === link.href
|
||||
: router.asPath.includes(link.href)
|
||||
link.name === "Settings"
|
||||
? router.asPath.includes(link.href)
|
||||
: router.asPath === link.href
|
||||
)
|
||||
? "bg-brand-surface-2 text-brand-base"
|
||||
: "text-brand-secondary hover:bg-brand-surface-2 hover:text-brand-secondary focus:bg-brand-surface-2 focus:text-brand-secondary"
|
||||
|
@ -106,13 +106,19 @@ export const MODULE_ISSUES_WITH_PARAMS = (moduleId: string, params?: any) => {
|
||||
|
||||
const paramsKey = paramsToKey(params);
|
||||
|
||||
return `MODULE_ISSUES_WITH_PARAMS_${moduleId}_${paramsKey.toUpperCase()}`;
|
||||
return `MODULE_ISSUES_WITH_PARAMS_${moduleId.toUpperCase()}_${paramsKey.toUpperCase()}`;
|
||||
};
|
||||
export const MODULE_DETAILS = (moduleId: string) => `MODULE_DETAILS_${moduleId.toUpperCase()}`;
|
||||
|
||||
export const VIEWS_LIST = (projectId: string) => `VIEWS_LIST_${projectId.toUpperCase()}`;
|
||||
export const VIEW_ISSUES = (viewId: string) => `VIEW_ISSUES_${viewId.toUpperCase()}`;
|
||||
export const VIEW_DETAILS = (viewId: string) => `VIEW_DETAILS_${viewId.toUpperCase()}`;
|
||||
export const VIEW_ISSUES = (viewId: string, params: any) => {
|
||||
if (!params) return `VIEW_ISSUES_${viewId.toUpperCase()}`;
|
||||
|
||||
const paramsKey = paramsToKey(params);
|
||||
|
||||
return `VIEW_ISSUES_${viewId.toUpperCase()}_${paramsKey.toUpperCase()}`;
|
||||
};
|
||||
|
||||
// Issues
|
||||
export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`;
|
||||
|
@ -12,6 +12,8 @@ export const orderArrayBy = (
|
||||
key: string,
|
||||
ordering: "ascending" | "descending" = "ascending"
|
||||
) => {
|
||||
if (!array || !Array.isArray(array) || array.length === 0) return [];
|
||||
|
||||
if (key[0] === "-") {
|
||||
ordering = "descending";
|
||||
key = key.slice(1);
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
MODULE_ISSUES_WITH_PARAMS,
|
||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
STATES_LIST,
|
||||
VIEW_ISSUES,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
const useIssuesView = () => {
|
||||
@ -40,7 +41,7 @@ const useIssuesView = () => {
|
||||
} = useContext(issueViewContext);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
const params: any = {
|
||||
order_by: orderBy,
|
||||
@ -99,6 +100,14 @@ const useIssuesView = () => {
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: viewIssues } = useSWR(
|
||||
workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null,
|
||||
workspaceSlug && projectId && viewId && params
|
||||
? () =>
|
||||
issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params)
|
||||
: null
|
||||
);
|
||||
|
||||
const { data: states } = useSWR(
|
||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
@ -106,7 +115,17 @@ const useIssuesView = () => {
|
||||
: null
|
||||
);
|
||||
const statesList = getStatesList(states ?? {});
|
||||
const stateIds = statesList.map((state) => state.id);
|
||||
const activeStatesList = statesList.filter(
|
||||
(state) => state.group === "started" || state.group === "unstarted"
|
||||
);
|
||||
const backlogStatesList = statesList.filter((state) => state.group === "backlog");
|
||||
|
||||
const stateIds =
|
||||
filters && filters?.type === "active"
|
||||
? activeStatesList.map((state) => state.id)
|
||||
: filters?.type === "backlog"
|
||||
? backlogStatesList.map((state) => state.id)
|
||||
: statesList.map((state) => state.id);
|
||||
|
||||
const emptyStatesObject: { [key: string]: [] } = {};
|
||||
for (let i = 0; i < stateIds.length; i++) {
|
||||
@ -118,7 +137,13 @@ const useIssuesView = () => {
|
||||
[key: string]: IIssue[];
|
||||
}
|
||||
| undefined = useMemo(() => {
|
||||
const issuesToGroup = cycleId ? cycleIssues : moduleId ? moduleIssues : projectIssues;
|
||||
const issuesToGroup = cycleId
|
||||
? cycleIssues
|
||||
: moduleId
|
||||
? moduleIssues
|
||||
: viewId
|
||||
? viewIssues
|
||||
: projectIssues;
|
||||
|
||||
if (Array.isArray(issuesToGroup)) return { allIssues: issuesToGroup };
|
||||
if (groupByProperty === "state")
|
||||
@ -129,9 +154,11 @@ const useIssuesView = () => {
|
||||
projectIssues,
|
||||
cycleIssues,
|
||||
moduleIssues,
|
||||
viewIssues,
|
||||
groupByProperty,
|
||||
cycleId,
|
||||
moduleId,
|
||||
viewId,
|
||||
emptyStatesObject,
|
||||
]);
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
import useSWR from "swr";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// services
|
||||
import projectService from "services/project.service";
|
||||
// helpers
|
||||
import { orderArrayBy } from "helpers/array.helper";
|
||||
// fetch-keys
|
||||
import { PROJECTS_LIST } from "constants/fetch-keys";
|
||||
|
||||
@ -20,7 +24,7 @@ const useProjects = () => {
|
||||
.filter((_item, index) => index < 3);
|
||||
|
||||
return {
|
||||
projects: projects || [],
|
||||
projects: orderArrayBy(projects ?? [], "is_favorite", "descending") || [],
|
||||
recentProjects: recentProjects || [],
|
||||
mutateProjects,
|
||||
};
|
||||
|
@ -30,7 +30,7 @@ const SettingsNavbar: React.FC<Props> = ({ profilePage = false }) => {
|
||||
href: `/${workspaceSlug}/settings/integrations`,
|
||||
},
|
||||
{
|
||||
label: "Import/ Export",
|
||||
label: "Import/Export",
|
||||
href: `/${workspaceSlug}/settings/import-export`,
|
||||
},
|
||||
];
|
||||
@ -94,7 +94,11 @@ const SettingsNavbar: React.FC<Props> = ({ profilePage = false }) => {
|
||||
<a>
|
||||
<div
|
||||
className={`rounded-3xl border border-brand-base px-5 py-1.5 text-sm sm:px-7 sm:py-2 sm:text-base ${
|
||||
router.asPath === link.href
|
||||
(
|
||||
link.label === "Import/Export"
|
||||
? router.asPath.includes(link.href)
|
||||
: router.asPath === link.href
|
||||
)
|
||||
? "border-brand-accent bg-brand-accent text-white"
|
||||
: "border-brand-base bg-brand-surface-2 hover:bg-brand-surface-1"
|
||||
}`}
|
||||
|
@ -164,7 +164,7 @@ const IssueDetailsPage: NextPage = () => {
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<CustomMenu ellipsis optionsPosition="left">
|
||||
<CustomMenu ellipsis position="left">
|
||||
{siblingIssues && siblingIssues.length > 0 ? (
|
||||
siblingIssues.map((issue: IIssue) => (
|
||||
<CustomMenu.MenuItem key={issue.id}>
|
||||
|
@ -563,7 +563,7 @@ const SinglePage: NextPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Loader>
|
||||
<Loader className="p-8">
|
||||
<Loader.Item height="200px" />
|
||||
</Loader>
|
||||
)}
|
||||
|
@ -18,10 +18,10 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
import { ContrastIcon, PeopleGroupIcon, ViewListIcon } from "components/icons";
|
||||
import { DocumentTextIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IProject } from "types";
|
||||
import { IFavoriteProject, IProject } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const featuresList = [
|
||||
@ -84,16 +84,29 @@ const FeaturesSettings: NextPage = () => {
|
||||
);
|
||||
|
||||
const handleSubmit = async (formData: Partial<IProject>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !projectDetails) return;
|
||||
|
||||
mutate<IProject>(
|
||||
PROJECT_DETAILS(projectId as string),
|
||||
(prevData) => ({ ...(prevData as IProject), ...formData }),
|
||||
false
|
||||
);
|
||||
if (projectDetails.is_favorite)
|
||||
mutate<IFavoriteProject[]>(
|
||||
FAVORITE_PROJECTS_LIST(workspaceSlug.toString()),
|
||||
(prevData) =>
|
||||
prevData?.map((p) => {
|
||||
if (p.project === projectId)
|
||||
return {
|
||||
...p,
|
||||
project_detail: {
|
||||
...p.project_detail,
|
||||
...formData,
|
||||
},
|
||||
};
|
||||
|
||||
return p;
|
||||
}),
|
||||
false
|
||||
);
|
||||
|
||||
mutate<IProject[]>(
|
||||
PROJECTS_LIST(workspaceSlug as string),
|
||||
PROJECTS_LIST(workspaceSlug.toString()),
|
||||
(prevData) =>
|
||||
prevData?.map((p) => {
|
||||
if (p.id === projectId)
|
||||
@ -107,19 +120,34 @@ const FeaturesSettings: NextPage = () => {
|
||||
false
|
||||
);
|
||||
|
||||
mutate<IProject>(
|
||||
PROJECT_DETAILS(projectId as string),
|
||||
(prevData) => ({ ...(prevData as IProject), ...formData }),
|
||||
false
|
||||
);
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "Project feature updated successfully.",
|
||||
});
|
||||
|
||||
await projectService
|
||||
.updateProject(workspaceSlug as string, projectId as string, formData)
|
||||
.then((res) => {
|
||||
.then(() => {
|
||||
mutate(
|
||||
projectDetails.is_favorite
|
||||
? FAVORITE_PROJECTS_LIST(workspaceSlug.toString())
|
||||
: PROJECTS_LIST(workspaceSlug.toString())
|
||||
);
|
||||
mutate(PROJECT_DETAILS(projectId as string));
|
||||
mutate(PROJECTS_LIST(workspaceSlug as string));
|
||||
setToastAlert({
|
||||
title: "Success!",
|
||||
type: "success",
|
||||
message: "Project features updated successfully.",
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Project feature could not be updated. Please try again.",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -229,7 +229,7 @@ const GeneralSettings: NextPage = () => {
|
||||
</div>
|
||||
<div className="col-span-12 sm:col-span-6">
|
||||
{watch("cover_image") ? (
|
||||
<div className="h-32 w-full rounded border p-1">
|
||||
<div className="h-32 w-full rounded border border-brand-base p-1">
|
||||
<div className="relative h-full w-full rounded">
|
||||
<Image
|
||||
src={watch("cover_image")!}
|
||||
|
@ -95,7 +95,7 @@ const SingleView: React.FC = () => {
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
>
|
||||
<PlusIcon className="w-4 h-4" />
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Issue
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
|
@ -119,7 +119,7 @@ const ProjectViews: NextPage = () => {
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<Loader className="space-y-3">
|
||||
<Loader className="space-y-3 p-8">
|
||||
<Loader.Item height="30px" />
|
||||
<Loader.Item height="30px" />
|
||||
<Loader.Item height="30px" />
|
||||
|
@ -19,12 +19,12 @@ import { Loader, EmptyState, PrimaryButton } from "components/ui";
|
||||
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
|
||||
// icons
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
// images
|
||||
import emptyProject from "public/empty-state/empty-project.svg";
|
||||
// types
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||
// image
|
||||
import emptyProject from "public/empty-state/empty-project.svg";
|
||||
|
||||
const ProjectsPage: NextPage = () => {
|
||||
// router
|
||||
|
Loading…
Reference in New Issue
Block a user