forked from github/plane
chore: updated display properties endpoint (#2486)
This commit is contained in:
parent
3a44d4bf35
commit
861ff4ae94
@ -2,4 +2,3 @@ export * from "./date-filter-modal";
|
||||
export * from "./date-filter-select";
|
||||
export * from "./filters-list";
|
||||
export * from "./workspace-filters-list";
|
||||
export * from "./issues-view-filter";
|
||||
|
@ -1,361 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// headless ui
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
// hooks
|
||||
import useIssuesProperties from "hooks/use-issue-properties";
|
||||
import useIssuesView from "hooks/use-issues-view";
|
||||
import useEstimateOption from "hooks/use-estimate-option";
|
||||
// components
|
||||
import { SelectFilters } from "components/views";
|
||||
// ui
|
||||
import { CustomMenu } from "components/ui";
|
||||
import { ToggleSwitch, Tooltip } from "@plane/ui";
|
||||
// icons
|
||||
import { CalendarDays, ChevronDown, GanttChart, Kanban, List, Sheet } from "lucide-react";
|
||||
// helpers
|
||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||
import { checkIfArraysHaveSameElements } from "helpers/array.helper";
|
||||
// types
|
||||
import { Properties, TIssueLayouts } from "types";
|
||||
// constants
|
||||
import { ISSUE_GROUP_BY_OPTIONS, ISSUE_ORDER_BY_OPTIONS, ISSUE_FILTER_OPTIONS } from "constants/issue";
|
||||
|
||||
const issueViewOptions: { type: TIssueLayouts; Icon: any }[] = [
|
||||
{
|
||||
type: "list",
|
||||
Icon: List,
|
||||
},
|
||||
{
|
||||
type: "kanban",
|
||||
Icon: Kanban,
|
||||
},
|
||||
{
|
||||
type: "calendar",
|
||||
Icon: CalendarDays,
|
||||
},
|
||||
{
|
||||
type: "spreadsheet",
|
||||
Icon: Sheet,
|
||||
},
|
||||
{
|
||||
type: "gantt_chart",
|
||||
Icon: GanttChart,
|
||||
},
|
||||
];
|
||||
|
||||
const issueViewForDraftIssues: { type: TIssueLayouts; Icon: any }[] = [
|
||||
{
|
||||
type: "list",
|
||||
Icon: List,
|
||||
},
|
||||
{
|
||||
type: "kanban",
|
||||
Icon: Kanban,
|
||||
},
|
||||
];
|
||||
|
||||
export const IssuesFilterView: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, viewId } = router.query;
|
||||
const isArchivedIssues = router.pathname.includes("archived-issues");
|
||||
const isDraftIssues = router.pathname?.split("/")?.[4] === "draft-issues";
|
||||
|
||||
const { displayFilters, setDisplayFilters, filters, setFilters, resetFilterToDefault, setNewFilterDefaultView } =
|
||||
useIssuesView();
|
||||
|
||||
const [properties, setProperties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
||||
|
||||
const { isEstimateActive } = useEstimateOption();
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{!isArchivedIssues && !isDraftIssues && (
|
||||
<div className="flex items-center gap-x-1">
|
||||
{issueViewOptions.map((option) => (
|
||||
<Tooltip
|
||||
key={option.type}
|
||||
tooltipContent={<span className="capitalize">{replaceUnderscoreIfSnakeCase(option.type)} Layout</span>}
|
||||
position="bottom"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
|
||||
displayFilters.layout === option.type
|
||||
? "bg-custom-sidebar-background-80"
|
||||
: "text-custom-sidebar-text-200"
|
||||
}`}
|
||||
onClick={() => setDisplayFilters({ layout: option.type })}
|
||||
>
|
||||
<option.Icon className="h-4 w-4" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{isDraftIssues && (
|
||||
<div className="flex items-center gap-x-1">
|
||||
{issueViewForDraftIssues.map((option) => (
|
||||
<Tooltip
|
||||
key={option.type}
|
||||
tooltipContent={<span className="capitalize">{replaceUnderscoreIfSnakeCase(option.type)} View</span>}
|
||||
position="bottom"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none hover:bg-custom-sidebar-background-80 duration-300 ${
|
||||
displayFilters.layout === option.type
|
||||
? "bg-custom-sidebar-background-80"
|
||||
: "text-custom-sidebar-text-200"
|
||||
}`}
|
||||
onClick={() => setDisplayFilters({ layout: option.type })}
|
||||
>
|
||||
<option.Icon
|
||||
sx={{
|
||||
fontSize: 16,
|
||||
}}
|
||||
className={option.type === "gantt_chart" ? "rotate-90" : ""}
|
||||
/>
|
||||
</button>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<SelectFilters
|
||||
filters={filters}
|
||||
onSelect={(option) => {
|
||||
const key = option.key as keyof typeof filters;
|
||||
|
||||
if (key === "start_date" || key === "target_date") {
|
||||
const valueExists = checkIfArraysHaveSameElements(filters[key] ?? [], option.value);
|
||||
|
||||
setFilters({
|
||||
[key]: valueExists ? null : option.value,
|
||||
});
|
||||
} else {
|
||||
const valueExists = filters[key]?.includes(option.value);
|
||||
|
||||
if (valueExists)
|
||||
setFilters(
|
||||
{
|
||||
[option.key]: ((filters[key] ?? []) as any[])?.filter((val) => val !== option.value),
|
||||
},
|
||||
!Boolean(viewId)
|
||||
);
|
||||
else
|
||||
setFilters(
|
||||
{
|
||||
[option.key]: [...((filters[key] ?? []) as any[]), option.value],
|
||||
},
|
||||
!Boolean(viewId)
|
||||
);
|
||||
}
|
||||
}}
|
||||
direction="left"
|
||||
height="rg"
|
||||
/>
|
||||
<Popover className="relative">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`group flex items-center gap-2 rounded-md border border-custom-border-200 px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
|
||||
open ? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100" : "text-custom-sidebar-text-200"
|
||||
}`}
|
||||
>
|
||||
Display
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</Popover.Button>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute right-0 z-30 mt-1 w-screen max-w-xs transform rounded-lg border border-custom-border-200 bg-custom-background-90 p-3 shadow-lg">
|
||||
<div className="relative divide-y-2 divide-custom-border-200">
|
||||
<div className="space-y-4 pb-3 text-xs">
|
||||
{displayFilters.layout !== "calendar" &&
|
||||
displayFilters.layout !== "spreadsheet" &&
|
||||
displayFilters.layout !== "gantt_chart" && (
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-custom-text-200">Group by</h4>
|
||||
<div className="w-28">
|
||||
<CustomMenu
|
||||
label={
|
||||
ISSUE_GROUP_BY_OPTIONS.find((option) => option.key === displayFilters.group_by)
|
||||
?.title ?? "Select"
|
||||
}
|
||||
className="!w-full"
|
||||
buttonClassName="w-full"
|
||||
>
|
||||
{ISSUE_GROUP_BY_OPTIONS.map((option) => {
|
||||
if (displayFilters.layout === "kanban" && option.key === null) return null;
|
||||
if (option.key === "project") return null;
|
||||
|
||||
if (isDraftIssues && option.key === "state_detail.group") return null;
|
||||
|
||||
return (
|
||||
<CustomMenu.MenuItem
|
||||
key={option.key}
|
||||
onClick={() => setDisplayFilters({ group_by: option.key })}
|
||||
>
|
||||
{option.title}
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
})}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{displayFilters.layout !== "calendar" && displayFilters.layout !== "spreadsheet" && (
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-custom-text-200">Order by</h4>
|
||||
<div className="w-28">
|
||||
<CustomMenu
|
||||
label={
|
||||
ISSUE_ORDER_BY_OPTIONS.find((option) => option.key === displayFilters.order_by)?.title ??
|
||||
"Select"
|
||||
}
|
||||
className="!w-full"
|
||||
buttonClassName="w-full"
|
||||
>
|
||||
{ISSUE_ORDER_BY_OPTIONS.map((option) =>
|
||||
displayFilters.group_by === "priority" && option.key === "priority" ? null : (
|
||||
<CustomMenu.MenuItem
|
||||
key={option.key}
|
||||
onClick={() => {
|
||||
setDisplayFilters({ order_by: option.key });
|
||||
}}
|
||||
>
|
||||
{option.title}
|
||||
</CustomMenu.MenuItem>
|
||||
)
|
||||
)}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isArchivedIssues && (
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-custom-text-200">Issue type</h4>
|
||||
<div className="w-28">
|
||||
<CustomMenu
|
||||
label={
|
||||
ISSUE_FILTER_OPTIONS.find((option) => option.key === displayFilters.type)?.title ??
|
||||
"Select"
|
||||
}
|
||||
className="!w-full"
|
||||
buttonClassName="w-full"
|
||||
>
|
||||
{ISSUE_FILTER_OPTIONS.map((option) => (
|
||||
<CustomMenu.MenuItem
|
||||
key={option.key}
|
||||
onClick={() =>
|
||||
setDisplayFilters({
|
||||
type: option.key,
|
||||
})
|
||||
}
|
||||
>
|
||||
{option.title}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayFilters.layout !== "calendar" && displayFilters.layout !== "spreadsheet" && (
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-custom-text-200">Show sub-issues</h4>
|
||||
<div className="w-28">
|
||||
<ToggleSwitch
|
||||
value={displayFilters.sub_issue ?? true}
|
||||
onChange={() => setDisplayFilters({ sub_issue: !displayFilters.sub_issue })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{displayFilters.layout !== "calendar" &&
|
||||
displayFilters.layout !== "spreadsheet" &&
|
||||
displayFilters.layout !== "gantt_chart" && (
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-custom-text-200">Show empty groups</h4>
|
||||
<div className="w-28">
|
||||
<ToggleSwitch
|
||||
value={displayFilters.show_empty_groups ?? true}
|
||||
onChange={() =>
|
||||
setDisplayFilters({
|
||||
show_empty_groups: !displayFilters.show_empty_groups,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{displayFilters.layout !== "calendar" &&
|
||||
displayFilters.layout !== "spreadsheet" &&
|
||||
displayFilters.layout !== "gantt_chart" && (
|
||||
<div className="relative flex justify-end gap-x-3">
|
||||
<button type="button" onClick={() => resetFilterToDefault()}>
|
||||
Reset to default
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="font-medium text-custom-primary"
|
||||
onClick={() => setNewFilterDefaultView()}
|
||||
>
|
||||
Set as default
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{displayFilters.layout !== "gantt_chart" && (
|
||||
<div className="space-y-2 py-3">
|
||||
<h4 className="text-sm text-custom-text-200">Display Properties</h4>
|
||||
<div className="flex flex-wrap items-center gap-2 text-custom-text-200">
|
||||
{Object.keys(properties).map((key) => {
|
||||
if (key === "estimate" && !isEstimateActive) return null;
|
||||
|
||||
if (
|
||||
displayFilters.layout === "spreadsheet" &&
|
||||
(key === "attachment_count" || key === "link" || key === "sub_issue_count")
|
||||
)
|
||||
return null;
|
||||
|
||||
if (displayFilters.layout !== "spreadsheet" && (key === "created_on" || key === "updated_on"))
|
||||
return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
className={`rounded border px-2 py-1 text-xs capitalize ${
|
||||
properties[key as keyof Properties]
|
||||
? "border-custom-primary bg-custom-primary text-white"
|
||||
: "border-custom-border-200"
|
||||
}`}
|
||||
onClick={() => setProperties(key as keyof Properties)}
|
||||
>
|
||||
{key === "key" ? "ID" : replaceUnderscoreIfSnakeCase(key)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,220 +0,0 @@
|
||||
// import React, { useCallback, useState } from "react";
|
||||
// import { useRouter } from "next/router";
|
||||
// import useSWR from "swr";
|
||||
// import { DragDropContext, DropResult } from "react-beautiful-dnd";
|
||||
// // services
|
||||
// import { ProjectStateService } from "services/project";
|
||||
// // hooks
|
||||
// import useUser from "hooks/use-user";
|
||||
// import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
// import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view";
|
||||
// // components
|
||||
// import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
||||
// import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
|
||||
// import { EmptyState } from "components/common";
|
||||
// // ui
|
||||
// import { Spinner } from "components/ui";
|
||||
// // icons
|
||||
// import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
// // images
|
||||
// import emptyIssue from "public/empty-state/issue.svg";
|
||||
// import emptyIssueArchive from "public/empty-state/issue-archive.svg";
|
||||
// // helpers
|
||||
// import { getStatesList } from "helpers/state.helper";
|
||||
// // types
|
||||
// import { IIssue, IIssueViewProps } from "types";
|
||||
// // fetch-keys
|
||||
// import { STATES_LIST } from "constants/fetch-keys";
|
||||
|
||||
// type Props = {
|
||||
// addIssueToDate: (date: string) => void;
|
||||
// addIssueToGroup: (groupTitle: string) => void;
|
||||
// disableUserActions: boolean;
|
||||
// dragDisabled?: boolean;
|
||||
// emptyState: {
|
||||
// title: string;
|
||||
// description?: string;
|
||||
// primaryButton?: {
|
||||
// icon: any;
|
||||
// text: string;
|
||||
// onClick: () => void;
|
||||
// };
|
||||
// secondaryButton?: React.ReactNode;
|
||||
// };
|
||||
// handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
||||
// handleDraftIssueAction?: (issue: IIssue, action: "edit" | "delete") => void;
|
||||
// handleOnDragEnd: (result: DropResult) => Promise<void>;
|
||||
// openIssuesListModal: (() => void) | null;
|
||||
// removeIssue: ((bridgeId: string, issueId: string) => void) | null;
|
||||
// disableAddIssueOption?: boolean;
|
||||
// trashBox: boolean;
|
||||
// setTrashBox: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
// viewProps: IIssueViewProps;
|
||||
// };
|
||||
|
||||
// const projectStateService = new ProjectStateService();
|
||||
|
||||
// export const AllViews: React.FC<Props> = ({
|
||||
// addIssueToDate,
|
||||
// addIssueToGroup,
|
||||
// disableUserActions,
|
||||
// dragDisabled = false,
|
||||
// emptyState,
|
||||
// handleIssueAction,
|
||||
// handleDraftIssueAction,
|
||||
// handleOnDragEnd,
|
||||
// openIssuesListModal,
|
||||
// removeIssue,
|
||||
// disableAddIssueOption = false,
|
||||
// trashBox,
|
||||
// setTrashBox,
|
||||
// viewProps,
|
||||
// }) => {
|
||||
// const router = useRouter();
|
||||
// const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
||||
|
||||
// const [myIssueProjectId, setMyIssueProjectId] = useState<string | null>(null);
|
||||
|
||||
// const { user } = useUser();
|
||||
// const { memberRole } = useProjectMyMembership();
|
||||
|
||||
// const { groupedIssues, isEmpty, displayFilters } = viewProps;
|
||||
|
||||
// const { spreadsheetIssues, mutateIssues } = useSpreadsheetIssuesView();
|
||||
|
||||
// const { data: stateGroups } = useSWR(
|
||||
// workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
// workspaceSlug ? () => projectStateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
// );
|
||||
// const states = getStatesList(stateGroups);
|
||||
|
||||
// const handleMyIssueOpen = (issue: IIssue) => {
|
||||
// setMyIssueProjectId(issue.project);
|
||||
// };
|
||||
|
||||
// const handleTrashBox = useCallback(
|
||||
// (isDragging: boolean) => {
|
||||
// if (isDragging && !trashBox) setTrashBox(true);
|
||||
// },
|
||||
// [trashBox, setTrashBox]
|
||||
// );
|
||||
|
||||
// return (
|
||||
// <DragDropContext onDragEnd={handleOnDragEnd}>
|
||||
// <StrictModeDroppable droppableId="trashBox">
|
||||
// {(provided, snapshot) => (
|
||||
// <div
|
||||
// className={`${
|
||||
// trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
||||
// } fixed top-4 left-1/2 -translate-x-1/2 z-40 w-72 flex items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-custom-background-100 px-3 py-5 text-xs font-medium italic text-red-500 ${
|
||||
// snapshot.isDraggingOver ? "bg-red-500 blur-2xl opacity-70" : ""
|
||||
// } transition duration-300`}
|
||||
// ref={provided.innerRef}
|
||||
// {...provided.droppableProps}
|
||||
// >
|
||||
// <TrashIcon className="h-4 w-4" />
|
||||
// Drop here to delete the issue.
|
||||
// </div>
|
||||
// )}
|
||||
// </StrictModeDroppable>
|
||||
// {groupedIssues ? (
|
||||
// !isEmpty ||
|
||||
// displayFilters?.layout === "kanban" ||
|
||||
// displayFilters?.layout === "calendar" ||
|
||||
// displayFilters?.layout === "gantt_chart" ? (
|
||||
// <>
|
||||
// {displayFilters?.layout === "list" ? (
|
||||
// <AllLists
|
||||
// states={states}
|
||||
// addIssueToGroup={addIssueToGroup}
|
||||
// handleIssueAction={handleIssueAction}
|
||||
// handleDraftIssueAction={handleDraftIssueAction}
|
||||
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||
// removeIssue={removeIssue}
|
||||
// myIssueProjectId={myIssueProjectId}
|
||||
// handleMyIssueOpen={handleMyIssueOpen}
|
||||
// disableUserActions={disableUserActions}
|
||||
// disableAddIssueOption={disableAddIssueOption}
|
||||
// user={user}
|
||||
// userAuth={memberRole}
|
||||
// viewProps={viewProps}
|
||||
// />
|
||||
// ) : displayFilters?.layout === "kanban" ? (
|
||||
// <AllBoards
|
||||
// addIssueToGroup={addIssueToGroup}
|
||||
// disableUserActions={disableUserActions}
|
||||
// disableAddIssueOption={disableAddIssueOption}
|
||||
// dragDisabled={dragDisabled}
|
||||
// handleIssueAction={handleIssueAction}
|
||||
// handleDraftIssueAction={handleDraftIssueAction}
|
||||
// handleTrashBox={handleTrashBox}
|
||||
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||
// myIssueProjectId={myIssueProjectId}
|
||||
// handleMyIssueOpen={handleMyIssueOpen}
|
||||
// removeIssue={removeIssue}
|
||||
// states={states}
|
||||
// user={user}
|
||||
// userAuth={memberRole}
|
||||
// viewProps={viewProps}
|
||||
// />
|
||||
// ) : displayFilters?.layout === "calendar" ? (
|
||||
// <CalendarView
|
||||
// handleIssueAction={handleIssueAction}
|
||||
// addIssueToDate={addIssueToDate}
|
||||
// disableUserActions={disableUserActions}
|
||||
// user={user}
|
||||
// userAuth={memberRole}
|
||||
// />
|
||||
// ) : displayFilters?.layout === "spreadsheet" ? (
|
||||
// <SpreadsheetView
|
||||
// handleIssueAction={handleIssueAction}
|
||||
// spreadsheetIssues={spreadsheetIssues}
|
||||
// mutateIssues={mutateIssues}
|
||||
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||
// disableUserActions={disableUserActions}
|
||||
// user={user}
|
||||
// userAuth={memberRole}
|
||||
// />
|
||||
// ) : (
|
||||
// displayFilters?.layout === "gantt_chart" && <GanttChartView disableUserActions={disableUserActions} />
|
||||
// )}
|
||||
// </>
|
||||
// ) : router.pathname.includes("archived-issues") ? (
|
||||
// <EmptyState
|
||||
// title="Archived Issues will be shown here"
|
||||
// description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here."
|
||||
// image={emptyIssueArchive}
|
||||
// primaryButton={{
|
||||
// text: "Go to Automation Settings",
|
||||
// onClick: () => {
|
||||
// router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`);
|
||||
// },
|
||||
// }}
|
||||
// />
|
||||
// ) : (
|
||||
// <EmptyState
|
||||
// title={emptyState.title}
|
||||
// description={emptyState.description}
|
||||
// image={emptyIssue}
|
||||
// primaryButton={
|
||||
// emptyState.primaryButton
|
||||
// ? {
|
||||
// icon: emptyState.primaryButton.icon,
|
||||
// text: emptyState.primaryButton.text,
|
||||
// onClick: emptyState.primaryButton.onClick,
|
||||
// }
|
||||
// : undefined
|
||||
// }
|
||||
// secondaryButton={emptyState.secondaryButton}
|
||||
// />
|
||||
// )
|
||||
// ) : (
|
||||
// <div className="flex h-full w-full items-center justify-center">
|
||||
// <Spinner />
|
||||
// </div>
|
||||
// )}
|
||||
// </DragDropContext>
|
||||
// );
|
||||
// };
|
||||
|
||||
export const AllViews = () => <></>;
|
@ -1,2 +1 @@
|
||||
export * from "./issues-view";
|
||||
export * from "./inline-issue-create-wrapper";
|
||||
|
@ -1,588 +0,0 @@
|
||||
// import { useCallback, useEffect, useState, FC } from "react";
|
||||
// import { useRouter } from "next/router";
|
||||
// import useSWR, { mutate } from "swr";
|
||||
// import { DropResult } from "react-beautiful-dnd";
|
||||
// // services
|
||||
// import { IssueService, IssueLabelService } from "services/issue";
|
||||
// import { ProjectStateService } from "services/project";
|
||||
// import { ModuleService } from "services/module.service";
|
||||
// import { TrackEventService } from "services/track_event.service";
|
||||
// // hooks
|
||||
// import useToast from "hooks/use-toast";
|
||||
// import useIssuesView from "hooks/use-issues-view";
|
||||
// import useUserAuth from "hooks/use-user-auth";
|
||||
// import useIssuesProperties from "hooks/use-issue-properties";
|
||||
// import useProjectMembers from "hooks/use-project-members";
|
||||
// // components
|
||||
// import { FiltersList } from "components/core";
|
||||
// import {
|
||||
// CreateUpdateIssueModal,
|
||||
// DeleteIssueModal,
|
||||
// DeleteDraftIssueModal,
|
||||
// CreateUpdateDraftIssueModal,
|
||||
// } from "components/issues";
|
||||
// import { CreateUpdateViewModal } from "components/views";
|
||||
// // ui
|
||||
// import { Button } from "@plane/ui";
|
||||
// // icons
|
||||
// import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
// // helpers
|
||||
// import { getStatesList } from "helpers/state.helper";
|
||||
// import { orderArrayBy } from "helpers/array.helper";
|
||||
// // types
|
||||
// import { IIssue, IIssueFilterOptions, IState, TIssuePriorities } from "types";
|
||||
// // fetch-keys
|
||||
// import {
|
||||
// CYCLE_DETAILS,
|
||||
// CYCLE_ISSUES_WITH_PARAMS,
|
||||
// MODULE_DETAILS,
|
||||
// MODULE_ISSUES_WITH_PARAMS,
|
||||
// PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||
// PROJECT_ISSUE_LABELS,
|
||||
// STATES_LIST,
|
||||
// } from "constants/fetch-keys";
|
||||
|
||||
// type Props = {
|
||||
// openIssuesListModal?: () => void;
|
||||
// disableUserActions?: boolean;
|
||||
// };
|
||||
|
||||
// const issueService = new IssueService();
|
||||
// const issueLabelService = new IssueLabelService();
|
||||
// const projectStateService = new ProjectStateService();
|
||||
// const moduleService = new ModuleService();
|
||||
// const trackEventService = new TrackEventService();
|
||||
|
||||
// export const IssuesView: FC<Props> = () => {
|
||||
// // const { openIssuesListModal, disableUserActions = false } = props;
|
||||
// // create issue modal
|
||||
// const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||
// const [createViewModal, setCreateViewModal] = useState<any>(null);
|
||||
// const [preloadedData, setPreloadedData] = useState<
|
||||
// (Partial<IIssue> & { actionType: "createIssue" | "edit" | "delete" }) | undefined
|
||||
// >(undefined);
|
||||
|
||||
// // update issue modal
|
||||
// const [editIssueModal, setEditIssueModal] = useState(false);
|
||||
// const [issueToEdit, setIssueToEdit] = useState<(IIssue & { actionType: "edit" | "delete" }) | undefined>(undefined);
|
||||
|
||||
// // delete issue modal
|
||||
// const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
// const [issueToDelete, setIssueToDelete] = useState<IIssue | null>(null);
|
||||
|
||||
// // trash box
|
||||
// // const [trashBox, setTrashBox] = useState(false);
|
||||
|
||||
// // selected draft issue
|
||||
// const [selectedDraftIssue, setSelectedDraftIssue] = useState<IIssue | null>(null);
|
||||
// const [selectedDraftForDelete, setSelectDraftForDelete] = useState<IIssue | null>(null);
|
||||
|
||||
// const router = useRouter();
|
||||
// const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
// const isDraftIssues = router.pathname?.split("/")?.[4] === "draft-issues";
|
||||
// // const isArchivedIssues = router.pathname?.split("/")?.[4] === "archived-issues";
|
||||
|
||||
// const { user } = useUserAuth();
|
||||
|
||||
// const { setToastAlert } = useToast();
|
||||
|
||||
// const { groupedByIssues, mutateIssues, displayFilters, filters, isEmpty, setFilters, params, setDisplayFilters } =
|
||||
// useIssuesView();
|
||||
// const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
||||
|
||||
// const { data: stateGroups } = useSWR(
|
||||
// workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
||||
// workspaceSlug ? () => projectStateService.getStates(workspaceSlug as string, projectId as string) : null
|
||||
// );
|
||||
// const states = getStatesList(stateGroups);
|
||||
|
||||
// const { data: labels } = useSWR(
|
||||
// workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null,
|
||||
// workspaceSlug && projectId
|
||||
// ? () => issueLabelService.getProjectIssueLabels(workspaceSlug.toString(), projectId.toString())
|
||||
// : null
|
||||
// );
|
||||
|
||||
// const { members } = useProjectMembers(workspaceSlug?.toString(), projectId?.toString());
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!isDraftIssues) return;
|
||||
|
||||
// if (
|
||||
// displayFilters.layout === "calendar" ||
|
||||
// displayFilters.layout === "gantt_chart" ||
|
||||
// displayFilters.layout === "spreadsheet"
|
||||
// )
|
||||
// setDisplayFilters({ layout: "list" });
|
||||
// }, [isDraftIssues, displayFilters, setDisplayFilters]);
|
||||
|
||||
// const handleDeleteIssue = useCallback(
|
||||
// (issue: IIssue) => {
|
||||
// setDeleteIssueModal(true);
|
||||
// setIssueToDelete(issue);
|
||||
// },
|
||||
// [setDeleteIssueModal, setIssueToDelete]
|
||||
// );
|
||||
|
||||
// const handleDraftIssueClick = useCallback((issue: any) => setSelectedDraftIssue(issue), []);
|
||||
// const handleDraftIssueDelete = useCallback((issue: any) => setSelectDraftForDelete(issue), []);
|
||||
|
||||
// const handleOnDragEnd = useCallback(
|
||||
// async (result: DropResult) => {
|
||||
// setTrashBox(false);
|
||||
|
||||
// if (!result.destination || !workspaceSlug || !projectId || !groupedByIssues) return;
|
||||
|
||||
// const { source, destination } = result;
|
||||
|
||||
// const draggedItem = groupedByIssues[source.droppableId][source.index];
|
||||
|
||||
// if (destination.droppableId === "trashBox") {
|
||||
// handleDeleteIssue(draggedItem);
|
||||
// } else {
|
||||
// if (displayFilters.order_by === "sort_order") {
|
||||
// let newSortOrder = draggedItem.sort_order;
|
||||
|
||||
// const destinationGroupArray = groupedByIssues[destination.droppableId];
|
||||
|
||||
// if (destinationGroupArray.length !== 0) {
|
||||
// // check if dropping in the same group
|
||||
// if (source.droppableId === destination.droppableId) {
|
||||
// // check if dropping at beginning
|
||||
// if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
// // check if dropping at last
|
||||
// else if (destination.index === destinationGroupArray.length - 1)
|
||||
// newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
// else {
|
||||
// if (destination.index > source.index)
|
||||
// newSortOrder =
|
||||
// (destinationGroupArray[source.index + 1].sort_order +
|
||||
// destinationGroupArray[source.index + 2].sort_order) /
|
||||
// 2;
|
||||
// else if (destination.index < source.index)
|
||||
// newSortOrder =
|
||||
// (destinationGroupArray[source.index - 1].sort_order +
|
||||
// destinationGroupArray[source.index - 2].sort_order) /
|
||||
// 2;
|
||||
// }
|
||||
// } else {
|
||||
// // check if dropping at beginning
|
||||
// if (destination.index === 0) newSortOrder = destinationGroupArray[0].sort_order - 10000;
|
||||
// // check if dropping at last
|
||||
// else if (destination.index === destinationGroupArray.length)
|
||||
// newSortOrder = destinationGroupArray[destinationGroupArray.length - 1].sort_order + 10000;
|
||||
// else
|
||||
// newSortOrder =
|
||||
// (destinationGroupArray[destination.index - 1].sort_order +
|
||||
// destinationGroupArray[destination.index].sort_order) /
|
||||
// 2;
|
||||
// }
|
||||
// }
|
||||
|
||||
// draggedItem.sort_order = newSortOrder;
|
||||
// }
|
||||
|
||||
// const destinationGroup = destination.droppableId; // destination group id
|
||||
|
||||
// if (displayFilters.order_by === "sort_order" || source.droppableId !== destination.droppableId) {
|
||||
// // different group/column;
|
||||
|
||||
// // source.droppableId !== destination.droppableId -> even if order by is not sort_order,
|
||||
// // if the issue is moved to a different group, then we will change the group of the
|
||||
// // dragged item(or issue)
|
||||
|
||||
// if (displayFilters.group_by === "priority") draggedItem.priority = destinationGroup as TIssuePriorities;
|
||||
// else if (displayFilters.group_by === "state") {
|
||||
// draggedItem.state = destinationGroup;
|
||||
// draggedItem.state_detail = states?.find((s) => s.id === destinationGroup) as IState;
|
||||
// }
|
||||
// }
|
||||
|
||||
// const sourceGroup = source.droppableId; // source group id
|
||||
|
||||
// mutate<{
|
||||
// [key: string]: IIssue[];
|
||||
// }>(
|
||||
// cycleId
|
||||
// ? CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)
|
||||
// : moduleId
|
||||
// ? MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)
|
||||
// : PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params),
|
||||
// (prevData) => {
|
||||
// if (!prevData) return prevData;
|
||||
|
||||
// const sourceGroupArray = [...groupedByIssues[sourceGroup]];
|
||||
// const destinationGroupArray = [...groupedByIssues[destinationGroup]];
|
||||
|
||||
// sourceGroupArray.splice(source.index, 1);
|
||||
// destinationGroupArray.splice(destination.index, 0, draggedItem);
|
||||
|
||||
// return {
|
||||
// ...prevData,
|
||||
// [sourceGroup]: orderArrayBy(sourceGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
// [destinationGroup]: orderArrayBy(destinationGroupArray, displayFilters.order_by ?? "-created_at"),
|
||||
// };
|
||||
// },
|
||||
// false
|
||||
// );
|
||||
|
||||
// // patch request
|
||||
// issueService
|
||||
// .patchIssue(
|
||||
// workspaceSlug as string,
|
||||
// projectId as string,
|
||||
// draggedItem.id,
|
||||
// {
|
||||
// priority: draggedItem.priority,
|
||||
// state: draggedItem.state,
|
||||
// sort_order: draggedItem.sort_order,
|
||||
// },
|
||||
// user
|
||||
// )
|
||||
// .then((response) => {
|
||||
// const sourceStateBeforeDrag = states?.find((state) => state.name === source.droppableId);
|
||||
|
||||
// if (sourceStateBeforeDrag?.group !== "completed" && response?.state_detail?.group === "completed")
|
||||
// trackEventService.trackIssueMarkedAsDoneEvent(
|
||||
// {
|
||||
// workspaceSlug,
|
||||
// workspaceId: draggedItem.workspace,
|
||||
// projectName: draggedItem.project_detail.name,
|
||||
// projectIdentifier: draggedItem.project_detail.identifier,
|
||||
// projectId,
|
||||
// issueId: draggedItem.id,
|
||||
// },
|
||||
// user
|
||||
// );
|
||||
|
||||
// if (cycleId) {
|
||||
// mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
|
||||
// mutate(CYCLE_DETAILS(cycleId as string));
|
||||
// }
|
||||
// if (moduleId) {
|
||||
// mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
|
||||
// mutate(MODULE_DETAILS(moduleId as string));
|
||||
// }
|
||||
// mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params));
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// [
|
||||
// displayFilters.group_by,
|
||||
// displayFilters.order_by,
|
||||
// workspaceSlug,
|
||||
// cycleId,
|
||||
// moduleId,
|
||||
// groupedByIssues,
|
||||
// projectId,
|
||||
// handleDeleteIssue,
|
||||
// params,
|
||||
// states,
|
||||
// user,
|
||||
// ]
|
||||
// );
|
||||
|
||||
// const addIssueToGroup = useCallback(
|
||||
// (groupTitle: string) => {
|
||||
// setCreateIssueModal(true);
|
||||
|
||||
// let preloadedValue: string | string[] = groupTitle;
|
||||
|
||||
// if (displayFilters.group_by === "labels") {
|
||||
// if (groupTitle === "None") preloadedValue = [];
|
||||
// else preloadedValue = [groupTitle];
|
||||
// }
|
||||
|
||||
// if (displayFilters.group_by)
|
||||
// setPreloadedData({
|
||||
// [displayFilters.group_by]: preloadedValue,
|
||||
// actionType: "createIssue",
|
||||
// });
|
||||
// else setPreloadedData({ actionType: "createIssue" });
|
||||
// },
|
||||
// [displayFilters.group_by, setCreateIssueModal, setPreloadedData]
|
||||
// );
|
||||
|
||||
// const addIssueToDate = useCallback(
|
||||
// (date: string) => {
|
||||
// setCreateIssueModal(true);
|
||||
// setPreloadedData({
|
||||
// target_date: date,
|
||||
// actionType: "createIssue",
|
||||
// });
|
||||
// },
|
||||
// [setCreateIssueModal, setPreloadedData]
|
||||
// );
|
||||
|
||||
// const makeIssueCopy = useCallback(
|
||||
// (issue: IIssue) => {
|
||||
// setCreateIssueModal(true);
|
||||
|
||||
// setPreloadedData({ ...issue, name: `${issue.name} (Copy)`, actionType: "createIssue" });
|
||||
// },
|
||||
// [setCreateIssueModal, setPreloadedData]
|
||||
// );
|
||||
|
||||
// const handleEditIssue = useCallback(
|
||||
// (issue: IIssue) => {
|
||||
// setEditIssueModal(true);
|
||||
// setIssueToEdit({
|
||||
// ...issue,
|
||||
// actionType: "edit",
|
||||
// cycle: issue.issue_cycle ? issue.issue_cycle.cycle : null,
|
||||
// module: issue.issue_module ? issue.issue_module.module : null,
|
||||
// });
|
||||
// },
|
||||
// [setEditIssueModal, setIssueToEdit]
|
||||
// );
|
||||
|
||||
// const handleIssueAction = useCallback(
|
||||
// (issue: IIssue, action: "copy" | "edit" | "delete") => {
|
||||
// if (action === "copy") makeIssueCopy(issue);
|
||||
// else if (action === "edit") handleEditIssue(issue);
|
||||
// else if (action === "delete") handleDeleteIssue(issue);
|
||||
// },
|
||||
// [makeIssueCopy, handleEditIssue, handleDeleteIssue]
|
||||
// );
|
||||
|
||||
// const handleDraftIssueAction = useCallback(
|
||||
// (issue: IIssue, action: "edit" | "delete") => {
|
||||
// if (action === "edit") handleDraftIssueClick(issue);
|
||||
// else if (action === "delete") handleDraftIssueDelete(issue);
|
||||
// },
|
||||
// [handleDraftIssueClick, handleDraftIssueDelete]
|
||||
// );
|
||||
|
||||
// const removeIssueFromCycle = useCallback(
|
||||
// (bridgeId: string, issueId: string) => {
|
||||
// if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
|
||||
// mutate(
|
||||
// CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params),
|
||||
// (prevData: any) => {
|
||||
// if (!prevData) return prevData;
|
||||
// if (displayFilters.group_by) {
|
||||
// const filteredData: any = {};
|
||||
// for (const key in prevData) {
|
||||
// filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId);
|
||||
// }
|
||||
// return filteredData;
|
||||
// } else {
|
||||
// const filteredData = prevData.filter((i: any) => i.id !== issueId);
|
||||
// return filteredData;
|
||||
// }
|
||||
// },
|
||||
// false
|
||||
// );
|
||||
|
||||
// issueService
|
||||
// .removeIssueFromCycle(workspaceSlug as string, projectId as string, cycleId as string, bridgeId)
|
||||
// .then(() => {
|
||||
// setToastAlert({
|
||||
// title: "Success",
|
||||
// message: "Issue removed successfully.",
|
||||
// type: "success",
|
||||
// });
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.log(e);
|
||||
// });
|
||||
// },
|
||||
// [displayFilters.group_by, workspaceSlug, projectId, cycleId, params, setToastAlert]
|
||||
// );
|
||||
|
||||
// const removeIssueFromModule = useCallback(
|
||||
// (bridgeId: string, issueId: string) => {
|
||||
// if (!workspaceSlug || !projectId || !moduleId) return;
|
||||
|
||||
// mutate(
|
||||
// MODULE_ISSUES_WITH_PARAMS(moduleId as string, params),
|
||||
// (prevData: any) => {
|
||||
// if (!prevData) return prevData;
|
||||
// if (displayFilters.group_by) {
|
||||
// const filteredData: any = {};
|
||||
// for (const key in prevData) {
|
||||
// filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId);
|
||||
// }
|
||||
// return filteredData;
|
||||
// } else {
|
||||
// const filteredData = prevData.filter((item: any) => item.id !== issueId);
|
||||
// return filteredData;
|
||||
// }
|
||||
// },
|
||||
// false
|
||||
// );
|
||||
|
||||
// moduleService
|
||||
// .removeIssueFromModule(workspaceSlug as string, projectId as string, moduleId as string, bridgeId)
|
||||
// .then(() => {
|
||||
// setToastAlert({
|
||||
// title: "Success",
|
||||
// message: "Issue removed successfully.",
|
||||
// type: "success",
|
||||
// });
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.log(e);
|
||||
// });
|
||||
// },
|
||||
// [displayFilters.group_by, workspaceSlug, projectId, moduleId, params, setToastAlert]
|
||||
// );
|
||||
|
||||
// const nullFilters = Object.keys(filters).filter((key) => filters[key as keyof IIssueFilterOptions] === null);
|
||||
|
||||
// const areFiltersApplied = Object.keys(filters).length > 0 && nullFilters.length !== Object.keys(filters).length;
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <CreateUpdateViewModal
|
||||
// isOpen={createViewModal !== null}
|
||||
// handleClose={() => setCreateViewModal(null)}
|
||||
// preLoadedData={createViewModal}
|
||||
// user={user}
|
||||
// />
|
||||
// <CreateUpdateIssueModal
|
||||
// isOpen={createIssueModal && preloadedData?.actionType === "createIssue"}
|
||||
// handleClose={() => setCreateIssueModal(false)}
|
||||
// prePopulateData={{
|
||||
// ...preloadedData,
|
||||
// }}
|
||||
// />
|
||||
// <CreateUpdateDraftIssueModal
|
||||
// isOpen={selectedDraftIssue !== null}
|
||||
// handleClose={() => setSelectedDraftIssue(null)}
|
||||
// data={
|
||||
// selectedDraftIssue
|
||||
// ? {
|
||||
// ...selectedDraftIssue,
|
||||
// is_draft: true,
|
||||
// }
|
||||
// : null
|
||||
// }
|
||||
// fieldsToShow={["all"]}
|
||||
// />
|
||||
// <CreateUpdateIssueModal
|
||||
// isOpen={editIssueModal && issueToEdit?.actionType !== "delete"}
|
||||
// handleClose={() => setEditIssueModal(false)}
|
||||
// data={issueToEdit}
|
||||
// />
|
||||
// <DeleteIssueModal
|
||||
// handleClose={() => setDeleteIssueModal(false)}
|
||||
// isOpen={deleteIssueModal}
|
||||
// data={issueToDelete}
|
||||
// user={user}
|
||||
// />
|
||||
// <DeleteDraftIssueModal
|
||||
// data={selectedDraftForDelete}
|
||||
// isOpen={selectedDraftForDelete !== null}
|
||||
// handleClose={() => setSelectDraftForDelete(null)}
|
||||
// />
|
||||
|
||||
// {areFiltersApplied && (
|
||||
// <>
|
||||
// <div className="flex items-center justify-between gap-2 px-5 pt-3 pb-0">
|
||||
// <FiltersList
|
||||
// filters={filters}
|
||||
// setFilters={(updatedFilter) => setFilters(updatedFilter, !Boolean(viewId))}
|
||||
// labels={labels}
|
||||
// members={members?.map((m: any) => m.member)}
|
||||
// states={states}
|
||||
// clearAllFilters={() =>
|
||||
// setFilters({
|
||||
// assignees: null,
|
||||
// created_by: null,
|
||||
// labels: null,
|
||||
// priority: null,
|
||||
// state: null,
|
||||
// state_group: null,
|
||||
// start_date: null,
|
||||
// target_date: null,
|
||||
// })
|
||||
// }
|
||||
// />
|
||||
// <Button
|
||||
// variant="primary"
|
||||
// prependIcon={!viewId && <PlusIcon />}
|
||||
// onClick={() => {
|
||||
// if (viewId) {
|
||||
// setFilters({}, true);
|
||||
// setToastAlert({
|
||||
// title: "View updated",
|
||||
// message: "Your view has been updated",
|
||||
// type: "success",
|
||||
// });
|
||||
// } else
|
||||
// setCreateViewModal({
|
||||
// query: filters,
|
||||
// });
|
||||
// }}
|
||||
// >
|
||||
// {viewId ? "Update" : "Save"} view
|
||||
// </Button>
|
||||
// </div>
|
||||
// {<div className="mt-3 border-t border-custom-border-200" />}
|
||||
// </>
|
||||
// )}
|
||||
// {/* <AllViews
|
||||
// addIssueToDate={addIssueToDate}
|
||||
// addIssueToGroup={addIssueToGroup}
|
||||
// disableUserActions={disableUserActions}
|
||||
// dragDisabled={
|
||||
// displayFilters.group_by === "created_by" ||
|
||||
// displayFilters.group_by === "labels" ||
|
||||
// displayFilters.group_by === "state_detail.group" ||
|
||||
// displayFilters.group_by === "assignees"
|
||||
// }
|
||||
// emptyState={{
|
||||
// title: isDraftIssues
|
||||
// ? "Draft issues will appear here"
|
||||
// : cycleId
|
||||
// ? "Cycle issues will appear here"
|
||||
// : moduleId
|
||||
// ? "Module issues will appear here"
|
||||
// : "Project issues will appear here",
|
||||
// description: isDraftIssues
|
||||
// ? "Draft issues are issues that are not yet created."
|
||||
// : "Issues help you track individual pieces of work. With Issues, keep track of what's going on, who is working on it, and what's done.",
|
||||
// primaryButton: !isDraftIssues
|
||||
// ? {
|
||||
// icon: <PlusIcon className="h-4 w-4" />,
|
||||
// text: "New Issue",
|
||||
// onClick: () => {
|
||||
// const e = new KeyboardEvent("keydown", {
|
||||
// key: "c",
|
||||
// });
|
||||
// document.dispatchEvent(e);
|
||||
// },
|
||||
// }
|
||||
// : undefined,
|
||||
// secondaryButton:
|
||||
// cycleId || moduleId ? (
|
||||
// <Button variant="neutral-primary" prependIcon={<PlusIcon />} onClick={openIssuesListModal ?? (() => {})}>
|
||||
// Add an existing issue
|
||||
// </Button>
|
||||
// ) : null,
|
||||
// }}
|
||||
// handleOnDragEnd={handleOnDragEnd}
|
||||
// handleIssueAction={handleIssueAction}
|
||||
// handleDraftIssueAction={handleDraftIssueAction}
|
||||
// openIssuesListModal={openIssuesListModal ?? null}
|
||||
// removeIssue={cycleId ? removeIssueFromCycle : moduleId ? removeIssueFromModule : null}
|
||||
// trashBox={trashBox}
|
||||
// setTrashBox={setTrashBox}
|
||||
// viewProps={{
|
||||
// groupedIssues: groupedByIssues,
|
||||
// displayFilters,
|
||||
// isEmpty,
|
||||
// mutateIssues,
|
||||
// params,
|
||||
// properties,
|
||||
// }}
|
||||
// disableAddIssueOption={isArchivedIssues}
|
||||
// /> */}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
export const IssuesView = () => <></>;
|
@ -7,14 +7,14 @@ import useToast from "hooks/use-toast";
|
||||
// helpers
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
// types
|
||||
import { IIssue, Properties } from "types";
|
||||
import { IIssue, IIssueDisplayProperties } from "types";
|
||||
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
projectId: string;
|
||||
expanded: boolean;
|
||||
handleToggleExpand: (issueId: string) => void;
|
||||
properties: Properties;
|
||||
properties: IIssueDisplayProperties;
|
||||
handleEditIssue: (issue: IIssue) => void;
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
disableUserActions: boolean;
|
||||
|
@ -5,14 +5,14 @@ import { IssueColumn } from "components/issues";
|
||||
// hooks
|
||||
import useSubIssue from "hooks/use-sub-issue";
|
||||
// types
|
||||
import { IIssue, Properties } from "types";
|
||||
import { IIssue, IIssueDisplayProperties } from "types";
|
||||
|
||||
type Props = {
|
||||
issue: IIssue;
|
||||
projectId: string;
|
||||
expandedIssues: string[];
|
||||
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
properties: Properties;
|
||||
properties: IIssueDisplayProperties;
|
||||
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
||||
disableUserActions: boolean;
|
||||
nestingLevel?: number;
|
||||
|
@ -110,7 +110,6 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
||||
<div className="flex-shrink-0 text-sm">
|
||||
<IssueProperty
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
parentIssue={parentIssue}
|
||||
issue={issue}
|
||||
user={user}
|
||||
|
@ -10,8 +10,6 @@ import { TrackEventService } from "services/track_event.service";
|
||||
import { ViewDueDateSelect, ViewStartDateSelect } from "components/issues";
|
||||
import { MembersSelect, PrioritySelect } from "components/project";
|
||||
import { StateSelect } from "components/states";
|
||||
// hooks
|
||||
import useIssuesProperties from "hooks/use-issue-properties";
|
||||
// types
|
||||
import { IUser, IIssue, IState } from "types";
|
||||
// fetch-keys
|
||||
@ -19,7 +17,6 @@ import { SUB_ISSUES } from "constants/fetch-keys";
|
||||
|
||||
export interface IIssueProperty {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
parentIssue: IIssue;
|
||||
issue: IIssue;
|
||||
user: IUser | undefined;
|
||||
@ -30,153 +27,153 @@ export interface IIssueProperty {
|
||||
const issueService = new IssueService();
|
||||
const trackEventService = new TrackEventService();
|
||||
|
||||
export const IssueProperty: React.FC<IIssueProperty> = observer(
|
||||
({ workspaceSlug, projectId, parentIssue, issue, user, editable }) => {
|
||||
const [properties] = useIssuesProperties(workspaceSlug, projectId);
|
||||
export const IssueProperty: React.FC<IIssueProperty> = observer((props) => {
|
||||
const { workspaceSlug, parentIssue, issue, user, editable } = props;
|
||||
|
||||
const { project: projectStore } = useMobxStore();
|
||||
const { project: projectStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
const handlePriorityChange = (data: any) => {
|
||||
partialUpdateIssue({ priority: data });
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_PRIORITY",
|
||||
user as IUser
|
||||
);
|
||||
};
|
||||
const displayProperties = issueFilterStore.userDisplayProperties ?? {};
|
||||
|
||||
const handleStateChange = (data: IState) => {
|
||||
partialUpdateIssue({
|
||||
state: data.id,
|
||||
state_detail: data,
|
||||
});
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_STATE",
|
||||
user as IUser
|
||||
);
|
||||
};
|
||||
|
||||
const handleAssigneeChange = (data: string[]) => {
|
||||
partialUpdateIssue({ assignees_list: data, assignees: data });
|
||||
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_ASSIGNEE",
|
||||
user as IUser
|
||||
);
|
||||
};
|
||||
|
||||
const partialUpdateIssue = async (data: Partial<IIssue>) => {
|
||||
mutate(
|
||||
workspaceSlug && parentIssue ? SUB_ISSUES(parentIssue.id) : null,
|
||||
(elements: any) => {
|
||||
const _elements = { ...elements };
|
||||
const _issues = _elements.sub_issues.map((element: IIssue) =>
|
||||
element.id === issue.id ? { ...element, ...data } : element
|
||||
);
|
||||
_elements["sub_issues"] = [..._issues];
|
||||
return _elements;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const issueResponse = await issueService.patchIssue(workspaceSlug as string, issue.project, issue.id, data, user);
|
||||
|
||||
mutate(
|
||||
SUB_ISSUES(parentIssue.id),
|
||||
(elements: any) => {
|
||||
const _elements = elements.sub_issues.map((element: IIssue) =>
|
||||
element.id === issue.id ? issueResponse : element
|
||||
);
|
||||
elements["sub_issues"] = _elements;
|
||||
return elements;
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-1">
|
||||
{properties.priority && (
|
||||
<div className="flex-shrink-0">
|
||||
<PrioritySelect
|
||||
value={issue.priority}
|
||||
onChange={handlePriorityChange}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{properties.state && (
|
||||
<div className="flex-shrink-0">
|
||||
<StateSelect
|
||||
value={issue.state_detail}
|
||||
stateGroups={projectStore.states ? projectStore.states[issue.project] : undefined}
|
||||
onChange={(data) => handleStateChange(data)}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{properties.start_date && issue.start_date && (
|
||||
<div className="flex-shrink-0 w-[104px]">
|
||||
<ViewStartDateSelect
|
||||
issue={issue}
|
||||
onChange={(val) => partialUpdateIssue({ start_date: val })}
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{properties.due_date && issue.target_date && (
|
||||
<div className="flex-shrink-0 w-[104px]">
|
||||
{user && (
|
||||
<ViewDueDateSelect
|
||||
issue={issue}
|
||||
onChange={(val) => partialUpdateIssue({ target_date: val })}
|
||||
disabled={!editable}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{properties.assignee && (
|
||||
<div className="flex-shrink-0">
|
||||
<MembersSelect
|
||||
value={issue.assignees}
|
||||
onChange={(val) => handleAssigneeChange(val)}
|
||||
members={projectStore.members ? (projectStore.members[issue.project] ?? []).map((m) => m.member) : []}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
const handlePriorityChange = (data: any) => {
|
||||
partialUpdateIssue({ priority: data });
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_PRIORITY",
|
||||
user as IUser
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleStateChange = (data: IState) => {
|
||||
partialUpdateIssue({
|
||||
state: data.id,
|
||||
state_detail: data,
|
||||
});
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_STATE",
|
||||
user as IUser
|
||||
);
|
||||
};
|
||||
|
||||
const handleAssigneeChange = (data: string[]) => {
|
||||
partialUpdateIssue({ assignees_list: data, assignees: data });
|
||||
|
||||
trackEventService.trackIssuePartialPropertyUpdateEvent(
|
||||
{
|
||||
workspaceSlug,
|
||||
workspaceId: issue.workspace,
|
||||
projectId: issue.project_detail.id,
|
||||
projectIdentifier: issue.project_detail.identifier,
|
||||
projectName: issue.project_detail.name,
|
||||
issueId: issue.id,
|
||||
},
|
||||
"ISSUE_PROPERTY_UPDATE_ASSIGNEE",
|
||||
user as IUser
|
||||
);
|
||||
};
|
||||
|
||||
const partialUpdateIssue = async (data: Partial<IIssue>) => {
|
||||
mutate(
|
||||
workspaceSlug && parentIssue ? SUB_ISSUES(parentIssue.id) : null,
|
||||
(elements: any) => {
|
||||
const _elements = { ...elements };
|
||||
const _issues = _elements.sub_issues.map((element: IIssue) =>
|
||||
element.id === issue.id ? { ...element, ...data } : element
|
||||
);
|
||||
_elements["sub_issues"] = [..._issues];
|
||||
return _elements;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
const issueResponse = await issueService.patchIssue(workspaceSlug as string, issue.project, issue.id, data, user);
|
||||
|
||||
mutate(
|
||||
SUB_ISSUES(parentIssue.id),
|
||||
(elements: any) => {
|
||||
const _elements = elements.sub_issues.map((element: IIssue) =>
|
||||
element.id === issue.id ? issueResponse : element
|
||||
);
|
||||
elements["sub_issues"] = _elements;
|
||||
return elements;
|
||||
},
|
||||
true
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-1">
|
||||
{displayProperties.priority && (
|
||||
<div className="flex-shrink-0">
|
||||
<PrioritySelect
|
||||
value={issue.priority}
|
||||
onChange={handlePriorityChange}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayProperties.state && (
|
||||
<div className="flex-shrink-0">
|
||||
<StateSelect
|
||||
value={issue.state_detail}
|
||||
stateGroups={projectStore.states ? projectStore.states[issue.project] : undefined}
|
||||
onChange={(data) => handleStateChange(data)}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayProperties.start_date && issue.start_date && (
|
||||
<div className="flex-shrink-0 w-[104px]">
|
||||
<ViewStartDateSelect
|
||||
issue={issue}
|
||||
onChange={(val) => partialUpdateIssue({ start_date: val })}
|
||||
disabled={!editable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayProperties.due_date && issue.target_date && (
|
||||
<div className="flex-shrink-0 w-[104px]">
|
||||
{user && (
|
||||
<ViewDueDateSelect
|
||||
issue={issue}
|
||||
onChange={(val) => partialUpdateIssue({ target_date: val })}
|
||||
disabled={!editable}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayProperties.assignee && (
|
||||
<div className="flex-shrink-0">
|
||||
<MembersSelect
|
||||
value={issue.assignees}
|
||||
onChange={(val) => handleAssigneeChange(val)}
|
||||
members={projectStore.members ? (projectStore.members[issue.project] ?? []).map((m) => m.member) : []}
|
||||
hideDropdownArrow
|
||||
disabled={!editable}
|
||||
multiple
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,108 +0,0 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import useSWR from "swr";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// types
|
||||
import { IssuePriorities, Properties } from "types";
|
||||
|
||||
const issueService = new IssueService();
|
||||
|
||||
const initialValues: Properties = {
|
||||
assignee: true,
|
||||
start_date: true,
|
||||
due_date: true,
|
||||
key: true,
|
||||
labels: true,
|
||||
priority: true,
|
||||
state: true,
|
||||
sub_issue_count: true,
|
||||
attachment_count: true,
|
||||
link: true,
|
||||
estimate: true,
|
||||
created_on: true,
|
||||
updated_on: true,
|
||||
};
|
||||
|
||||
const useIssuesProperties = (workspaceSlug?: string, projectId?: string) => {
|
||||
const [properties, setProperties] = useState<Properties>(initialValues);
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const { data: issueProperties, mutate: mutateIssueProperties } = useSWR<IssuePriorities>(
|
||||
workspaceSlug && projectId ? `/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-properties/` : null,
|
||||
workspaceSlug && projectId ? () => issueService.getIssueProperties(workspaceSlug, projectId) : null
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!issueProperties || !workspaceSlug || !projectId || !user) return;
|
||||
|
||||
setProperties({ ...initialValues, ...issueProperties.properties });
|
||||
|
||||
if (Object.keys(issueProperties).length === 0)
|
||||
issueService.createIssueProperties(workspaceSlug, projectId, {
|
||||
properties: { ...initialValues },
|
||||
user: user.id,
|
||||
});
|
||||
else if (Object.keys(issueProperties?.properties).length === 0)
|
||||
issueService.patchIssueProperties(workspaceSlug, projectId, issueProperties.id, {
|
||||
properties: { ...initialValues },
|
||||
user: user.id,
|
||||
});
|
||||
}, [issueProperties, workspaceSlug, projectId, user]);
|
||||
|
||||
const updateIssueProperties = useCallback(
|
||||
(key: keyof Properties) => {
|
||||
if (!workspaceSlug || !user) return;
|
||||
|
||||
setProperties((prev) => ({ ...prev, [key]: !prev[key] }));
|
||||
|
||||
if (issueProperties && projectId) {
|
||||
mutateIssueProperties(
|
||||
(prev: any) =>
|
||||
({
|
||||
...prev,
|
||||
properties: { ...prev?.properties, [key]: !prev?.properties?.[key] },
|
||||
} as IssuePriorities),
|
||||
false
|
||||
);
|
||||
if (Object.keys(issueProperties).length > 0) {
|
||||
issueService.patchIssueProperties(workspaceSlug, projectId, issueProperties.id, {
|
||||
properties: {
|
||||
...issueProperties.properties,
|
||||
[key]: !issueProperties.properties[key],
|
||||
},
|
||||
user: user.id,
|
||||
});
|
||||
} else {
|
||||
issueService.createIssueProperties(workspaceSlug, projectId, {
|
||||
properties: { ...initialValues },
|
||||
user: user.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[workspaceSlug, projectId, issueProperties, user, mutateIssueProperties]
|
||||
);
|
||||
|
||||
const newProperties: Properties = {
|
||||
assignee: properties.assignee,
|
||||
start_date: properties.start_date,
|
||||
due_date: properties.due_date,
|
||||
key: properties.key,
|
||||
labels: properties.labels,
|
||||
priority: properties.priority,
|
||||
state: properties.state,
|
||||
sub_issue_count: properties.sub_issue_count,
|
||||
attachment_count: properties.attachment_count,
|
||||
link: properties.link,
|
||||
estimate: properties.estimate,
|
||||
created_on: properties.created_on,
|
||||
updated_on: properties.updated_on,
|
||||
};
|
||||
|
||||
return [newProperties, updateIssueProperties] as const;
|
||||
};
|
||||
|
||||
export default useIssuesProperties;
|
@ -11,8 +11,6 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy";
|
||||
import { IssueViewContextProvider } from "contexts/issue-view.context";
|
||||
// helper
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// components
|
||||
import { IssuesFilterView, IssuesView } from "components/core";
|
||||
// ui
|
||||
import { ArchiveIcon, BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
||||
// icons
|
||||
@ -51,11 +49,11 @@ const ProjectArchivedIssues: NextPage = () => {
|
||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Archived Issues`} />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<div className="flex items-center gap-2">
|
||||
<IssuesFilterView />
|
||||
</div>
|
||||
}
|
||||
// right={
|
||||
// <div className="flex items-center gap-2">
|
||||
// <IssuesFilterView />
|
||||
// </div>
|
||||
// }
|
||||
>
|
||||
<div className="h-full w-full flex flex-col">
|
||||
<div className="flex items-center ga-1 px-4 py-2.5 shadow-sm border-b border-custom-border-200">
|
||||
@ -70,7 +68,7 @@ const ProjectArchivedIssues: NextPage = () => {
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<IssuesView />
|
||||
{/* <IssuesView /> */}
|
||||
</div>
|
||||
</ProjectAuthorizationWrapper>
|
||||
</IssueViewContextProvider>
|
||||
|
@ -11,8 +11,6 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy";
|
||||
import { IssueViewContextProvider } from "contexts/issue-view.context";
|
||||
// helper
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// components
|
||||
import { IssuesFilterView, IssuesView } from "components/core";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs } from "@plane/ui";
|
||||
// icons
|
||||
@ -51,11 +49,11 @@ const ProjectDraftIssues: NextPage = () => {
|
||||
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Draft Issues`} />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<div className="flex items-center gap-2">
|
||||
<IssuesFilterView />
|
||||
</div>
|
||||
}
|
||||
// right={
|
||||
// <div className="flex items-center gap-2">
|
||||
// <IssuesFilterView />
|
||||
// </div>
|
||||
// }
|
||||
>
|
||||
<div className="h-full w-full flex flex-col">
|
||||
<div className="flex items-center ga-1 px-4 py-2.5 shadow-sm border-b border-custom-border-200">
|
||||
@ -70,7 +68,6 @@ const ProjectDraftIssues: NextPage = () => {
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
<IssuesView />
|
||||
</div>
|
||||
</ProjectAuthorizationWrapper>
|
||||
</IssueViewContextProvider>
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { APIService } from "services/api.service";
|
||||
import { TrackEventService } from "services/track_event.service";
|
||||
// type
|
||||
import type { IUser, IIssue, IIssueActivity, ISubIssueResponse } from "types";
|
||||
import type { IUser, IIssue, IIssueActivity, ISubIssueResponse, IIssueDisplayProperties } from "types";
|
||||
// helper
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
|
||||
@ -62,14 +62,6 @@ export class IssueService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async getIssueProperties(workspaceSlug: string, projectId: string): Promise<any> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async addIssueToCycle(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -153,24 +145,20 @@ export class IssueService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async createIssueProperties(workspaceSlug: string, projectId: string, data: any): Promise<any> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-properties/`, data)
|
||||
async getIssueDisplayProperties(workspaceSlug: string, projectId: string): Promise<any> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-display-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async patchIssueProperties(
|
||||
async patchIssueDisplayProperties(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issuePropertyId: string,
|
||||
data: any
|
||||
data: IIssueDisplayProperties
|
||||
): Promise<any> {
|
||||
return this.patch(
|
||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-properties/` + `${issuePropertyId}/`,
|
||||
data
|
||||
)
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-display-properties/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
|
@ -136,14 +136,6 @@ export class ProjectService extends APIService {
|
||||
});
|
||||
}
|
||||
|
||||
async joinProjects(data: any): Promise<any> {
|
||||
return this.post("/api/users/me/invitations/projects/", data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async projectMembers(workspaceSlug: string, projectId: string): Promise<IProjectMember[]> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/project-members/`)
|
||||
.then((response) => response?.data)
|
||||
|
@ -45,7 +45,7 @@ export class IssueFilterStore implements IIssueFilterStore {
|
||||
error: any | null = null;
|
||||
|
||||
// observables
|
||||
userDisplayProperties: any = {};
|
||||
userDisplayProperties: IIssueDisplayProperties = {};
|
||||
userDisplayFilters: IIssueDisplayFilterOptions = {};
|
||||
userFilters: IIssueFilterOptions = {};
|
||||
defaultDisplayFilters: IIssueDisplayFilterOptions = {};
|
||||
@ -144,7 +144,7 @@ export class IssueFilterStore implements IIssueFilterStore {
|
||||
fetchUserProjectFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
try {
|
||||
const memberResponse = await this.projectService.projectMemberMe(workspaceSlug, projectId);
|
||||
const issueProperties = await this.issueService.getIssueProperties(workspaceSlug, projectId);
|
||||
const issueProperties = await this.issueService.getIssueDisplayProperties(workspaceSlug, projectId);
|
||||
|
||||
runInAction(() => {
|
||||
this.userFilters = memberResponse?.view_props?.filters;
|
||||
@ -207,7 +207,7 @@ export class IssueFilterStore implements IIssueFilterStore {
|
||||
projectId: string,
|
||||
properties: Partial<IIssueDisplayProperties>
|
||||
) => {
|
||||
const newProperties = {
|
||||
const newProperties: IIssueDisplayProperties = {
|
||||
...this.userDisplayProperties,
|
||||
...properties,
|
||||
};
|
||||
@ -217,7 +217,7 @@ export class IssueFilterStore implements IIssueFilterStore {
|
||||
this.userDisplayProperties = newProperties;
|
||||
});
|
||||
|
||||
// await this.issueService.patchIssueProperties(workspaceSlug, projectId, newProperties);
|
||||
await this.issueService.patchIssueDisplayProperties(workspaceSlug, projectId, newProperties);
|
||||
} catch (error) {
|
||||
this.fetchUserProjectFilters(workspaceSlug, projectId);
|
||||
|
||||
|
30
web/types/view-props.d.ts
vendored
30
web/types/view-props.d.ts
vendored
@ -84,19 +84,19 @@ export interface IIssueDisplayFilterOptions {
|
||||
type?: TIssueTypeFilters;
|
||||
}
|
||||
export interface IIssueDisplayProperties {
|
||||
assignee: boolean;
|
||||
start_date: boolean;
|
||||
due_date: boolean;
|
||||
labels: boolean;
|
||||
key: boolean;
|
||||
priority: boolean;
|
||||
state: boolean;
|
||||
sub_issue_count: boolean;
|
||||
link: boolean;
|
||||
attachment_count: boolean;
|
||||
estimate: boolean;
|
||||
created_on: boolean;
|
||||
updated_on: boolean;
|
||||
assignee?: boolean;
|
||||
start_date?: boolean;
|
||||
due_date?: boolean;
|
||||
labels?: boolean;
|
||||
key?: boolean;
|
||||
priority?: boolean;
|
||||
state?: boolean;
|
||||
sub_issue_count?: boolean;
|
||||
link?: boolean;
|
||||
attachment_count?: boolean;
|
||||
estimate?: boolean;
|
||||
created_on?: boolean;
|
||||
updated_on?: boolean;
|
||||
}
|
||||
|
||||
export interface IWorkspaceIssueFilterOptions {
|
||||
@ -142,10 +142,10 @@ export interface IProjectViewProps {
|
||||
export interface IWorkspaceViewProps {
|
||||
filters: IIssueFilterOptions;
|
||||
display_filters: IIssueDisplayFilterOptions | undefined;
|
||||
display_properties: Properties;
|
||||
display_properties: IIssueDisplayProperties;
|
||||
}
|
||||
export interface IWorkspaceGlobalViewProps {
|
||||
filters: IWorkspaceIssueFilterOptions;
|
||||
display_filters: IWorkspaceIssueDisplayFilterOptions | undefined;
|
||||
display_properties: Properties;
|
||||
display_properties: IIssueDisplayProperties;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user