forked from github/plane
issues rendering in all issue layouts fir profile and project issues and global issues store implementation (#2886)
* dev: draft and archived issue store * connect draft and archived issues * kanban for draft issues * fix filter store for calendar and kanban * dev: profile issues store and draft issues filters in header * disble issue creation for draft issues * dev: profile issues store filters * disable kanban properties in draft issues * dev: profile issues store filters * dev: seperated adding issues to the cycle and module as seperate methds in cycle and module store * dev: workspace profile issues store * dev: sub group issues in the swimlanes * profile issues and create issue connection * fix profile issues * fix spreadsheet issues * fix dissapearing project from create issue modal * page level modifications * fix additional bugs * dev: issues profile and global iisues and filters update * fix issue related bugs * fix project views for list and kanban * fix build errors --------- Co-authored-by: rahulramesha <rahulramesham@gmail.com>
This commit is contained in:
parent
eb78fd6088
commit
2bf7e63625
@ -55,6 +55,8 @@ export const CommandPalette: FC = observer(() => {
|
|||||||
toggleBulkDeleteIssueModal,
|
toggleBulkDeleteIssueModal,
|
||||||
isDeleteIssueModalOpen,
|
isDeleteIssueModalOpen,
|
||||||
toggleDeleteIssueModal,
|
toggleDeleteIssueModal,
|
||||||
|
|
||||||
|
createIssueStoreType,
|
||||||
} = commandPalette;
|
} = commandPalette;
|
||||||
|
|
||||||
const isAnyModalOpen = Boolean(
|
const isAnyModalOpen = Boolean(
|
||||||
@ -224,6 +226,7 @@ export const CommandPalette: FC = observer(() => {
|
|||||||
prePopulateData={
|
prePopulateData={
|
||||||
cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
|
cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
|
||||||
}
|
}
|
||||||
|
currentStore={createIssueStoreType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{issueId && issueDetails && (
|
{issueId && issueDetails && (
|
||||||
|
@ -20,6 +20,7 @@ import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOption
|
|||||||
// constants
|
// constants
|
||||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||||
import { EFilterType } from "store/issues/types";
|
import { EFilterType } from "store/issues/types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const CycleIssuesHeader: React.FC = observer(() => {
|
export const CycleIssuesHeader: React.FC = observer(() => {
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -33,7 +34,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
cycle: cycleStore,
|
cycle: cycleStore,
|
||||||
cycleIssueFilters: cycleIssueFiltersStore,
|
|
||||||
projectIssuesFilter: projectIssueFiltersStore,
|
projectIssuesFilter: projectIssueFiltersStore,
|
||||||
project: { currentProjectDetails },
|
project: { currentProjectDetails },
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
@ -190,11 +190,14 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
||||||
Analytics
|
Analytics
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => {
|
<Button
|
||||||
setTrackElement("CYCLE_PAGE_HEADER")
|
onClick={() => {
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
setTrackElement("CYCLE_PAGE_HEADER");
|
||||||
}
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.CYCLE);
|
||||||
} size="sm" prependIcon={<Plus />}>
|
}}
|
||||||
|
size="sm"
|
||||||
|
prependIcon={<Plus />}
|
||||||
|
>
|
||||||
Add Issue
|
Add Issue
|
||||||
</Button>
|
</Button>
|
||||||
<button
|
<button
|
||||||
|
@ -20,6 +20,7 @@ import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOption
|
|||||||
// constants
|
// constants
|
||||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||||
import { EFilterType } from "store/issues/types";
|
import { EFilterType } from "store/issues/types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ModuleIssuesHeader: React.FC = observer(() => {
|
export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -33,7 +34,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
module: moduleStore,
|
module: moduleStore,
|
||||||
projectIssuesFilter: projectIssueFiltersStore,
|
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
projectState: projectStateStore,
|
projectState: projectStateStore,
|
||||||
@ -191,11 +191,14 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
||||||
Analytics
|
Analytics
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => {
|
<Button
|
||||||
setTrackElement("MODULE_PAGE_HEADER")
|
onClick={() => {
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
setTrackElement("MODULE_PAGE_HEADER");
|
||||||
}
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.MODULE);
|
||||||
} size="sm" prependIcon={<Plus />}>
|
}}
|
||||||
|
size="sm"
|
||||||
|
prependIcon={<Plus />}
|
||||||
|
>
|
||||||
Add Issue
|
Add Issue
|
||||||
</Button>
|
</Button>
|
||||||
<button
|
<button
|
||||||
|
@ -1,20 +1,74 @@
|
|||||||
import { FC } from "react";
|
import { FC, useCallback } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||||
// helper
|
// helper
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { EFilterType } from "store/issues/types";
|
||||||
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||||
|
|
||||||
export const ProjectDraftIssueHeader: FC = observer(() => {
|
export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const { project: projectStore } = useMobxStore();
|
const {
|
||||||
const { currentProjectDetails } = projectStore;
|
project: { currentProjectDetails },
|
||||||
|
projectLabel: { projectLabels },
|
||||||
|
projectMember: { projectMembers },
|
||||||
|
projectState: projectStateStore,
|
||||||
|
projectDraftIssuesFilter: { issueFilters, updateFilters },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
|
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
|
const handleFiltersUpdate = useCallback(
|
||||||
|
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((val) => {
|
||||||
|
if (!newValues.includes(val)) newValues.push(val);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||||
|
else newValues.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFilters(workspaceSlug, projectId, EFilterType.FILTERS, { [key]: newValues });
|
||||||
|
},
|
||||||
|
[workspaceSlug, projectId, issueFilters, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleLayoutChange = useCallback(
|
||||||
|
(layout: TIssueLayouts) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
updateFilters(workspaceSlug, projectId, EFilterType.DISPLAY_FILTERS, { layout: layout });
|
||||||
|
},
|
||||||
|
[workspaceSlug, projectId, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDisplayFilters = useCallback(
|
||||||
|
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
updateFilters(workspaceSlug, projectId, EFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
||||||
|
},
|
||||||
|
[workspaceSlug, projectId, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDisplayProperties = useCallback(
|
||||||
|
(property: Partial<IIssueDisplayProperties>) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
updateFilters(workspaceSlug, projectId, EFilterType.DISPLAY_PROPERTIES, property);
|
||||||
|
},
|
||||||
|
[workspaceSlug, projectId, updateFilters]
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 h-[3.75rem] items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
<div className="relative flex w-full flex-shrink-0 flex-row z-10 h-[3.75rem] items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4">
|
||||||
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
<div className="flex items-center gap-2 flex-grow w-full whitespace-nowrap overflow-ellipsis">
|
||||||
@ -44,6 +98,37 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
|||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="ml-auto flex items-center gap-2">
|
||||||
|
<LayoutSelection
|
||||||
|
layouts={["list", "kanban"]}
|
||||||
|
onChange={(layout) => handleLayoutChange(layout)}
|
||||||
|
selectedLayout={activeLayout}
|
||||||
|
/>
|
||||||
|
<FiltersDropdown title="Filters" placement="bottom-end">
|
||||||
|
<FilterSelection
|
||||||
|
filters={issueFilters?.filters ?? {}}
|
||||||
|
handleFiltersUpdate={handleFiltersUpdate}
|
||||||
|
layoutDisplayFiltersOptions={
|
||||||
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||||
|
}
|
||||||
|
labels={projectLabels ?? undefined}
|
||||||
|
members={projectMembers?.map((m) => m.member)}
|
||||||
|
states={projectStateStore.states?.[projectId ?? ""] ?? undefined}
|
||||||
|
/>
|
||||||
|
</FiltersDropdown>
|
||||||
|
<FiltersDropdown title="Display" placement="bottom-end">
|
||||||
|
<DisplayFiltersSelection
|
||||||
|
layoutDisplayFiltersOptions={
|
||||||
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||||
|
}
|
||||||
|
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||||
|
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||||
|
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||||
|
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||||
|
/>
|
||||||
|
</FiltersDropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -17,6 +17,7 @@ import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
|||||||
// helper
|
// helper
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
import { EFilterType } from "store/issues/types";
|
import { EFilterType } from "store/issues/types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ProjectIssuesHeader: React.FC = observer(() => {
|
export const ProjectIssuesHeader: React.FC = observer(() => {
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -199,11 +200,14 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
<Button onClick={() => setAnalyticsModal(true)} variant="neutral-primary" size="sm">
|
||||||
Analytics
|
Analytics
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => {
|
<Button
|
||||||
setTrackElement("PROJECT_PAGE_HEADER");
|
onClick={() => {
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
setTrackElement("PROJECT_PAGE_HEADER");
|
||||||
}
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT);
|
||||||
} size="sm" prependIcon={<Plus />}>
|
}}
|
||||||
|
size="sm"
|
||||||
|
prependIcon={<Plus />}
|
||||||
|
>
|
||||||
Add Issue
|
Add Issue
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,7 +72,7 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
|||||||
<p className="text-sm text-custom-text-200">
|
<p className="text-sm text-custom-text-200">
|
||||||
Are you sure you want to delete issue{" "}
|
Are you sure you want to delete issue{" "}
|
||||||
<span className="break-words font-medium text-custom-text-100">
|
<span className="break-words font-medium text-custom-text-100">
|
||||||
{data?.project_detail.identifier}-{data?.sequence_id}
|
{data?.project_detail?.identifier}-{data?.sequence_id}
|
||||||
</span>
|
</span>
|
||||||
{""}? All of the data related to the issue will be permanently removed. This action cannot be
|
{""}? All of the data related to the issue will be permanently removed. This action cannot be
|
||||||
undone.
|
undone.
|
||||||
|
@ -228,7 +228,7 @@ export const IssueForm: FC<IssueFormProps> = observer((props) => {
|
|||||||
...defaultValues,
|
...defaultValues,
|
||||||
...initialData,
|
...initialData,
|
||||||
});
|
});
|
||||||
}, [setFocus, initialData, reset]);
|
}, [setFocus, reset]);
|
||||||
|
|
||||||
// update projectId in form when projectId changes
|
// update projectId in form when projectId changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -7,14 +7,28 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { CalendarChart } from "components/issues";
|
import { CalendarChart } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { ICycleIssuesStore, IModuleIssuesStore, IProjectIssuesStore, IViewIssuesStore } from "store/issues";
|
import {
|
||||||
import { IIssueCalendarViewStore, IssueStore } from "store/issue";
|
ICycleIssuesFilterStore,
|
||||||
|
ICycleIssuesStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IModuleIssuesStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IProjectIssuesStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
IViewIssuesStore,
|
||||||
|
} from "store/issues";
|
||||||
|
import { IIssueCalendarViewStore } from "store/issue";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { IGroupedIssues } from "store/issues/types";
|
import { IGroupedIssues } from "store/issues/types";
|
||||||
|
|
||||||
interface IBaseCalendarRoot {
|
interface IBaseCalendarRoot {
|
||||||
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
||||||
|
issuesFilterStore:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore;
|
||||||
calendarViewStore: IIssueCalendarViewStore;
|
calendarViewStore: IIssueCalendarViewStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
@ -26,10 +40,9 @@ interface IBaseCalendarRoot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||||
const { issueStore, calendarViewStore, QuickActions, issueActions, viewId } = props;
|
const { issueStore, issuesFilterStore, calendarViewStore, QuickActions, issueActions, viewId } = props;
|
||||||
const { projectIssuesFilter: issueFilterStore } = useMobxStore();
|
|
||||||
|
|
||||||
const displayFilters = issueFilterStore.issueFilters?.displayFilters;
|
const displayFilters = issuesFilterStore.issueFilters?.displayFilters;
|
||||||
|
|
||||||
const issues = issueStore.getIssues;
|
const issues = issueStore.getIssues;
|
||||||
const groupedIssueIds = (issueStore.getIssuesIds ?? {}) as IGroupedIssues;
|
const groupedIssueIds = (issueStore.getIssuesIds ?? {}) as IGroupedIssues;
|
||||||
@ -75,7 +88,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||||||
}
|
}
|
||||||
handleRemoveFromView={
|
handleRemoveFromView={
|
||||||
issueActions[EIssueActions.REMOVE]
|
issueActions[EIssueActions.REMOVE]
|
||||||
? async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.UPDATE)
|
? async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.REMOVE)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -10,7 +10,11 @@ import { EIssueActions } from "../../types";
|
|||||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||||
|
|
||||||
export const CycleCalendarLayout: React.FC = observer(() => {
|
export const CycleCalendarLayout: React.FC = observer(() => {
|
||||||
const { cycleIssues: cycleIssueStore, cycleIssueCalendarView: cycleIssueCalendarViewStore } = useMobxStore();
|
const {
|
||||||
|
cycleIssues: cycleIssueStore,
|
||||||
|
cycleIssuesFilter: cycleIssueFilterStore,
|
||||||
|
cycleIssueCalendarView: cycleIssueCalendarViewStore,
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||||
@ -34,6 +38,7 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<BaseCalendarRoot
|
<BaseCalendarRoot
|
||||||
issueStore={cycleIssueStore}
|
issueStore={cycleIssueStore}
|
||||||
|
issuesFilterStore={cycleIssueFilterStore}
|
||||||
calendarViewStore={cycleIssueCalendarViewStore}
|
calendarViewStore={cycleIssueCalendarViewStore}
|
||||||
QuickActions={CycleIssueQuickActions}
|
QuickActions={CycleIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
@ -10,7 +10,11 @@ import { EIssueActions } from "../../types";
|
|||||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||||
|
|
||||||
export const ModuleCalendarLayout: React.FC = observer(() => {
|
export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||||
const { moduleIssues: moduleIssueStore, moduleIssueCalendarView: moduleIssueCalendarViewStore } = useMobxStore();
|
const {
|
||||||
|
moduleIssues: moduleIssueStore,
|
||||||
|
moduleIssuesFilter: moduleIssueFilterStore,
|
||||||
|
moduleIssueCalendarView: moduleIssueCalendarViewStore,
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||||
@ -33,6 +37,7 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
|||||||
return (
|
return (
|
||||||
<BaseCalendarRoot
|
<BaseCalendarRoot
|
||||||
issueStore={moduleIssueStore}
|
issueStore={moduleIssueStore}
|
||||||
|
issuesFilterStore={moduleIssueFilterStore}
|
||||||
calendarViewStore={moduleIssueCalendarViewStore}
|
calendarViewStore={moduleIssueCalendarViewStore}
|
||||||
QuickActions={ModuleIssueQuickActions}
|
QuickActions={ModuleIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
@ -15,25 +15,26 @@ export const CalendarLayout: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
projectIssues: issueStore,
|
projectIssues: issueStore,
|
||||||
issueCalendarView: issueCalendarViewStore,
|
issueCalendarView: issueCalendarViewStore,
|
||||||
issueDetail: issueDetailStore,
|
projectIssuesFilter: projectIssueFiltersStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), issue.project, issue.id);
|
issueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseCalendarRoot
|
<BaseCalendarRoot
|
||||||
issueStore={issueStore}
|
issueStore={issueStore}
|
||||||
|
issuesFilterStore={projectIssueFiltersStore}
|
||||||
calendarViewStore={issueCalendarViewStore}
|
calendarViewStore={issueCalendarViewStore}
|
||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { useCallback } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { CalendarChart, ProjectIssueQuickActions } from "components/issues";
|
import { ProjectIssueQuickActions } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
@ -14,7 +12,7 @@ import { BaseCalendarRoot } from "../base-calendar-root";
|
|||||||
export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||||
const {
|
const {
|
||||||
viewIssues: projectViewIssuesStore,
|
viewIssues: projectViewIssuesStore,
|
||||||
issueDetail: issueDetailStore,
|
viewIssuesFilter: projectIssueViewFiltersStore,
|
||||||
projectViewIssueCalendarView: projectViewIssueCalendarViewStore,
|
projectViewIssueCalendarView: projectViewIssueCalendarViewStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
@ -25,18 +23,19 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
|||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
projectViewIssuesStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), issue.project, issue.id);
|
projectViewIssuesStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseCalendarRoot
|
<BaseCalendarRoot
|
||||||
issueStore={projectViewIssuesStore}
|
issueStore={projectViewIssuesStore}
|
||||||
|
issuesFilterStore={projectIssueViewFiltersStore}
|
||||||
calendarViewStore={projectViewIssueCalendarViewStore}
|
calendarViewStore={projectViewIssueCalendarViewStore}
|
||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
@ -14,6 +14,7 @@ import { Button } from "@plane/ui";
|
|||||||
import emptyIssue from "public/empty-state/issue.svg";
|
import emptyIssue from "public/empty-state/issue.svg";
|
||||||
// types
|
// types
|
||||||
import { ISearchIssueResponse } from "types";
|
import { ISearchIssueResponse } from "types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string | undefined;
|
workspaceSlug: string | undefined;
|
||||||
@ -26,7 +27,11 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
|
|||||||
// states
|
// states
|
||||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||||
|
|
||||||
const { cycleIssue: cycleIssueStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
cycleIssue: cycleIssueStore,
|
||||||
|
commandPalette: commandPaletteStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -63,9 +68,9 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
|
|||||||
text: "New issue",
|
text: "New issue",
|
||||||
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTrackElement("CYCLE_EMPTY_STATE")
|
setTrackElement("CYCLE_EMPTY_STATE");
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.CYCLE);
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
secondaryButton={
|
secondaryButton={
|
||||||
<Button
|
<Button
|
||||||
|
@ -6,9 +6,17 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// assets
|
// assets
|
||||||
import emptyIssue from "public/empty-state/issue.svg";
|
import emptyIssue from "public/empty-state/issue.svg";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
export const ProjectViewEmptyState: React.FC = observer(() => {
|
export const ProjectViewEmptyState: React.FC = observer(() => {
|
||||||
const { commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const router = useRouter();
|
||||||
|
const { viewId } = router.query as { viewId: string };
|
||||||
|
|
||||||
|
const {
|
||||||
|
commandPalette: commandPaletteStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full grid place-items-center">
|
<div className="h-full w-full grid place-items-center">
|
||||||
@ -21,8 +29,8 @@ export const ProjectViewEmptyState: React.FC = observer(() => {
|
|||||||
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTrackElement("VIEW_EMPTY_STATE");
|
setTrackElement("VIEW_EMPTY_STATE");
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT_VIEW);
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,9 +6,13 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// assets
|
// assets
|
||||||
import emptyIssue from "public/empty-state/issue.svg";
|
import emptyIssue from "public/empty-state/issue.svg";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ProjectEmptyState: React.FC = observer(() => {
|
export const ProjectEmptyState: React.FC = observer(() => {
|
||||||
const { commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
commandPalette: commandPaletteStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full grid place-items-center">
|
<div className="h-full w-full grid place-items-center">
|
||||||
@ -21,8 +25,8 @@ export const ProjectEmptyState: React.FC = observer(() => {
|
|||||||
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTrackElement("PROJECT_EMPTY_STATE");
|
setTrackElement("PROJECT_EMPTY_STATE");
|
||||||
commandPaletteStore.toggleCreateIssueModal(true)
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT);
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,23 +7,24 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { AppliedFiltersList } from "components/issues";
|
import { AppliedFiltersList } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssueFilterOptions } from "types";
|
import { IIssueFilterOptions } from "types";
|
||||||
|
import { EFilterType } from "store/issues/types";
|
||||||
|
|
||||||
export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
archivedIssueFilters: archivedIssueFiltersStore,
|
projectArchivedIssuesFilter: { issueFilters, updateFilters },
|
||||||
projectLabel: { projectLabels },
|
projectLabel: { projectLabels },
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
projectState: projectStateStore,
|
projectState: projectStateStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const userFilters = archivedIssueFiltersStore.userFilters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
|
||||||
// filters whose value not null or empty array
|
// filters whose value not null or empty array
|
||||||
const appliedFilters: IIssueFilterOptions = {};
|
const appliedFilters: IIssueFilterOptions = {};
|
||||||
Object.entries(userFilters).forEach(([key, value]) => {
|
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
if (Array.isArray(value) && value.length === 0) return;
|
if (Array.isArray(value) && value.length === 0) return;
|
||||||
@ -36,22 +37,18 @@ export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
|
|
||||||
// remove all values of the key if value is null
|
// remove all values of the key if value is null
|
||||||
if (!value) {
|
if (!value) {
|
||||||
archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, {
|
||||||
filters: {
|
[key]: null,
|
||||||
[key]: null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the passed value from the key
|
// remove the passed value from the key
|
||||||
let newValues = archivedIssueFiltersStore.userFilters?.[key] ?? [];
|
let newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
newValues = newValues.filter((val) => val !== value);
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, {
|
||||||
filters: {
|
[key]: newValues,
|
||||||
[key]: newValues,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,12 +56,12 @@ export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
const newFilters: IIssueFilterOptions = {};
|
const newFilters: IIssueFilterOptions = {};
|
||||||
Object.keys(userFilters).forEach((key) => {
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, {
|
||||||
filters: { ...newFilters },
|
...newFilters,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { AppliedFiltersList } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "types";
|
||||||
|
import { EFilterType } from "store/issues/types";
|
||||||
|
|
||||||
|
export const DraftIssueAppliedFiltersRoot: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
|
const {
|
||||||
|
projectDraftIssuesFilter: { issueFilters, updateFilters },
|
||||||
|
projectLabel: { projectLabels },
|
||||||
|
projectMember: { projectMembers },
|
||||||
|
projectState: projectStateStore,
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
|
const userFilters = issueFilters?.filters;
|
||||||
|
// filters whose value not null or empty array
|
||||||
|
const appliedFilters: IIssueFilterOptions = {};
|
||||||
|
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
||||||
|
if (!value) return;
|
||||||
|
|
||||||
|
if (Array.isArray(value) && value.length === 0) return;
|
||||||
|
|
||||||
|
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
// remove all values of the key if value is null
|
||||||
|
if (!value) {
|
||||||
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, {
|
||||||
|
[key]: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the passed value from the key
|
||||||
|
let newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, {
|
||||||
|
[key]: newValues,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAllFilters = () => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
|
const newFilters: IIssueFilterOptions = {};
|
||||||
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { ...newFilters });
|
||||||
|
};
|
||||||
|
|
||||||
|
// return if no filters are applied
|
||||||
|
if (Object.keys(appliedFilters).length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<AppliedFiltersList
|
||||||
|
appliedFilters={appliedFilters}
|
||||||
|
handleClearAllFilters={handleClearAllFilters}
|
||||||
|
handleRemoveFilter={handleRemoveFilter}
|
||||||
|
labels={projectLabels ?? []}
|
||||||
|
members={projectMembers?.map((m) => m.member)}
|
||||||
|
states={projectStateStore.states?.[projectId?.toString() ?? ""]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -4,3 +4,4 @@ export * from "./module-root";
|
|||||||
export * from "./project-view-root";
|
export * from "./project-view-root";
|
||||||
export * from "./project-root";
|
export * from "./project-root";
|
||||||
export * from "./archived-issue";
|
export * from "./archived-issue";
|
||||||
|
export * from "./profile-issues-root";
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { AppliedFiltersList } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "types";
|
||||||
|
import { EFilterType } from "store/issues/types";
|
||||||
|
|
||||||
|
export const ProfileIssuesAppliedFiltersRoot: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query as {
|
||||||
|
workspaceSlug: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
workspace: { workspaceLabels },
|
||||||
|
workspaceProfileIssuesFilter: { issueFilters, updateFilters },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
|
const userFilters = issueFilters?.filters;
|
||||||
|
|
||||||
|
// filters whose value not null or empty array
|
||||||
|
const appliedFilters: IIssueFilterOptions = {};
|
||||||
|
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
||||||
|
if (!value) return;
|
||||||
|
if (Array.isArray(value) && value.length === 0) return;
|
||||||
|
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
if (!value) {
|
||||||
|
updateFilters(workspaceSlug, EFilterType.FILTERS, { [key]: null });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
|
newValues = newValues.filter((val) => val !== value);
|
||||||
|
|
||||||
|
updateFilters(workspaceSlug, EFilterType.FILTERS, {
|
||||||
|
[key]: newValues,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClearAllFilters = () => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
const newFilters: IIssueFilterOptions = {};
|
||||||
|
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||||
|
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||||
|
});
|
||||||
|
updateFilters(workspaceSlug, EFilterType.FILTERS, { ...newFilters });
|
||||||
|
};
|
||||||
|
|
||||||
|
// return if no filters are applied
|
||||||
|
if (Object.keys(appliedFilters).length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<AppliedFiltersList
|
||||||
|
appliedFilters={appliedFilters}
|
||||||
|
handleClearAllFilters={handleClearAllFilters}
|
||||||
|
handleRemoveFilter={handleRemoveFilter}
|
||||||
|
labels={workspaceLabels ?? []}
|
||||||
|
members={[]}
|
||||||
|
states={[]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -57,10 +57,16 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
//Todo fix sort order in the structure
|
//Todo fix sort order in the structure
|
||||||
issueStore.updateIssue(workspaceSlug, issue.project, issue.id, {
|
issueStore.updateIssue(
|
||||||
start_date: payload.start_date,
|
workspaceSlug,
|
||||||
target_date: payload.target_date,
|
issue.project,
|
||||||
});
|
issue.id,
|
||||||
|
{
|
||||||
|
start_date: payload.start_date,
|
||||||
|
target_date: payload.target_date,
|
||||||
|
},
|
||||||
|
viewId
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAllowed = (projectDetails?.member_role || 0) >= EUserWorkspaceRoles.MEMBER;
|
const isAllowed = (projectDetails?.member_role || 0) >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { FC, useCallback, useState } from "react";
|
import { FC, useCallback, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { DragDropContext } from "@hello-pangea/dnd";
|
import { DragDropContext } from "@hello-pangea/dnd";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// mobx store
|
// mobx store
|
||||||
@ -9,7 +8,19 @@ import { Spinner } from "@plane/ui";
|
|||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { ICycleIssuesStore, IModuleIssuesStore, IProjectIssuesStore, IViewIssuesStore } from "store/issues";
|
import {
|
||||||
|
ICycleIssuesFilterStore,
|
||||||
|
ICycleIssuesStore,
|
||||||
|
IModuleIssuesFilterStore,
|
||||||
|
IModuleIssuesStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProfileIssuesStore,
|
||||||
|
IProjectDraftIssuesStore,
|
||||||
|
IProjectIssuesFilterStore,
|
||||||
|
IProjectIssuesStore,
|
||||||
|
IViewIssuesFilterStore,
|
||||||
|
IViewIssuesStore,
|
||||||
|
} from "store/issues";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
import { IIssueKanBanViewStore } from "store/issue";
|
import { IIssueKanBanViewStore } from "store/issue";
|
||||||
// constants
|
// constants
|
||||||
@ -17,9 +28,22 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
|||||||
//components
|
//components
|
||||||
import { KanBan } from "./default";
|
import { KanBan } from "./default";
|
||||||
import { KanBanSwimLanes } from "./swimlanes";
|
import { KanBanSwimLanes } from "./swimlanes";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IBaseKanBanLayout {
|
export interface IBaseKanBanLayout {
|
||||||
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
issueStore:
|
||||||
|
| IProjectIssuesStore
|
||||||
|
| IModuleIssuesStore
|
||||||
|
| ICycleIssuesStore
|
||||||
|
| IViewIssuesStore
|
||||||
|
| IProjectDraftIssuesStore
|
||||||
|
| IProfileIssuesStore;
|
||||||
|
issuesFilterStore:
|
||||||
|
| IProjectIssuesFilterStore
|
||||||
|
| IModuleIssuesFilterStore
|
||||||
|
| ICycleIssuesFilterStore
|
||||||
|
| IViewIssuesFilterStore
|
||||||
|
| IProfileIssuesFilterStore;
|
||||||
kanbanViewStore: IIssueKanBanViewStore;
|
kanbanViewStore: IIssueKanBanViewStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
@ -29,24 +53,33 @@ export interface IBaseKanBanLayout {
|
|||||||
};
|
};
|
||||||
showLoader?: boolean;
|
showLoader?: boolean;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBaseKanBanLayout) => {
|
export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBaseKanBanLayout) => {
|
||||||
const { issueStore, kanbanViewStore, QuickActions, issueActions, showLoader, viewId } = props;
|
const {
|
||||||
|
issueStore,
|
||||||
|
issuesFilterStore,
|
||||||
|
kanbanViewStore,
|
||||||
|
QuickActions,
|
||||||
|
issueActions,
|
||||||
|
showLoader,
|
||||||
|
viewId,
|
||||||
|
currentStore,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: { workspaceProjects },
|
project: { workspaceProjects },
|
||||||
projectLabel: { projectLabels },
|
projectLabel: { projectLabels },
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
projectState: projectStateStore,
|
projectState: projectStateStore,
|
||||||
projectIssuesFilter: issueFilterStore,
|
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issues = issueStore?.getIssues || {};
|
const issues = issueStore?.getIssues || {};
|
||||||
const issueIds = issueStore?.getIssuesIds || [];
|
const issueIds = issueStore?.getIssuesIds || [];
|
||||||
|
|
||||||
const displayFilters = issueFilterStore?.issueFilters?.displayFilters;
|
const displayFilters = issuesFilterStore?.issueFilters?.displayFilters;
|
||||||
const displayProperties = issueFilterStore?.issueFilters?.displayProperties || null;
|
const displayProperties = issuesFilterStore?.issueFilters?.displayProperties || null;
|
||||||
|
|
||||||
const sub_group_by: string | null = displayFilters?.sub_group_by || null;
|
const sub_group_by: string | null = displayFilters?.sub_group_by || null;
|
||||||
|
|
||||||
@ -60,6 +93,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
|
|
||||||
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
||||||
const onDragStart = () => {
|
const onDragStart = () => {
|
||||||
setIsDragStarted(true);
|
setIsDragStarted(true);
|
||||||
};
|
};
|
||||||
@ -103,7 +137,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{showLoader && issueStore?.loader === "mutation" && (
|
{showLoader && issueStore?.loader === "init-loader" && (
|
||||||
<div className="fixed top-16 right-2 z-30 bg-custom-background-80 shadow-custom-shadow-sm w-10 h-10 rounded flex justify-center items-center">
|
<div className="fixed top-16 right-2 z-30 bg-custom-background-80 shadow-custom-shadow-sm w-10 h-10 rounded flex justify-center items-center">
|
||||||
<Spinner className="w-5 h-5" />
|
<Spinner className="w-5 h-5" />
|
||||||
</div>
|
</div>
|
||||||
@ -144,11 +178,14 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
labels={projectLabels}
|
labels={projectLabels}
|
||||||
members={projectMembers?.map((m) => m.member) ?? null}
|
members={projectMembers?.map((m) => m.member) ?? null}
|
||||||
projects={workspaceProjects}
|
projects={workspaceProjects}
|
||||||
enableQuickIssueCreate
|
enableQuickIssueCreate={enableQuickAdd}
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={issueStore.quickAddIssue}
|
quickAddCallback={issueStore?.quickAddIssue}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={!enableIssueCreation}
|
||||||
|
isReadOnly={!enableInlineEditing}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<KanBanSwimLanes
|
<KanBanSwimLanes
|
||||||
@ -185,6 +222,10 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
projects={workspaceProjects}
|
projects={workspaceProjects}
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={true}
|
||||||
|
enableQuickIssueCreate={enableQuickAdd}
|
||||||
|
isReadOnly={!enableInlineEditing}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
|
@ -17,6 +17,7 @@ interface IssueBlockProps {
|
|||||||
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
|
quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
displayProperties: IIssueDisplayProperties | null;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
||||||
@ -30,6 +31,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
displayProperties,
|
||||||
|
isReadOnly,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const updateIssue = (sub_group_by: string | null, group_by: string | null, issueToUpdate: IIssue) => {
|
const updateIssue = (sub_group_by: string | null, group_by: string | null, issueToUpdate: IIssue) => {
|
||||||
@ -91,6 +93,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
|||||||
handleIssues={updateIssue}
|
handleIssues={updateIssue}
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@ interface IssueBlocksListProps {
|
|||||||
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => void;
|
||||||
quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
|
quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
displayProperties: IIssueDisplayProperties | null;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanbanIssueBlocksList: React.FC<IssueBlocksListProps> = (props) => {
|
export const KanbanIssueBlocksList: React.FC<IssueBlocksListProps> = (props) => {
|
||||||
@ -27,6 +28,7 @@ export const KanbanIssueBlocksList: React.FC<IssueBlocksListProps> = (props) =>
|
|||||||
handleIssues,
|
handleIssues,
|
||||||
quickActions,
|
quickActions,
|
||||||
displayProperties,
|
displayProperties,
|
||||||
|
isReadOnly,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -50,6 +52,7 @@ export const KanbanIssueBlocksList: React.FC<IssueBlocksListProps> = (props) =>
|
|||||||
columnId={columnId}
|
columnId={columnId}
|
||||||
sub_group_id={sub_group_id}
|
sub_group_id={sub_group_id}
|
||||||
isDragDisabled={isDragDisabled}
|
isDragDisabled={isDragDisabled}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -13,6 +13,7 @@ import { getValueFromObject } from "constants/issue";
|
|||||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IGroupByKanBan {
|
export interface IGroupByKanBan {
|
||||||
issues: IIssueResponse;
|
issues: IIssueResponse;
|
||||||
@ -40,6 +41,9 @@ export interface IGroupByKanBan {
|
|||||||
viewId?: string
|
viewId?: string
|
||||||
) => Promise<IIssue | undefined>;
|
) => Promise<IIssue | undefined>;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||||
@ -63,6 +67,9 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
isDragStarted,
|
isDragStarted,
|
||||||
quickAddCallback,
|
quickAddCallback,
|
||||||
viewId,
|
viewId,
|
||||||
|
disableIssueCreation,
|
||||||
|
isReadOnly,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const verticalAlignPosition = (_list: any) =>
|
const verticalAlignPosition = (_list: any) =>
|
||||||
@ -86,6 +93,8 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
issues_count={issueIds?.[getValueFromObject(_list, listKey) as string]?.length || 0}
|
issues_count={issueIds?.[getValueFromObject(_list, listKey) as string]?.length || 0}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -95,10 +104,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
verticalAlignPosition(_list) ? `w-[0px] overflow-hidden` : `w-full transition-all`
|
verticalAlignPosition(_list) ? `w-[0px] overflow-hidden` : `w-full transition-all`
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Droppable
|
<Droppable droppableId={`${getValueFromObject(_list, listKey) as string}__${sub_group_id}`}>
|
||||||
droppableId={`${getValueFromObject(_list, listKey) as string}__${sub_group_id}`}
|
|
||||||
isDropDisabled={isDragDisabled}
|
|
||||||
>
|
|
||||||
{(provided: any, snapshot: any) => (
|
{(provided: any, snapshot: any) => (
|
||||||
<div
|
<div
|
||||||
className={`w-full h-full relative transition-all ${
|
className={`w-full h-full relative transition-all ${
|
||||||
@ -118,6 +124,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
handleIssues={handleIssues}
|
handleIssues={handleIssues}
|
||||||
quickActions={quickActions}
|
quickActions={quickActions}
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
isDragDisabled && (
|
isDragDisabled && (
|
||||||
@ -149,7 +156,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isDragStarted && isDragDisabled && (
|
{/* {isDragStarted && isDragDisabled && (
|
||||||
<div className="invisible group-hover:visible transition-all text-sm absolute top-12 bottom-10 left-0 right-0 bg-custom-background-100/40 text-center">
|
<div className="invisible group-hover:visible transition-all text-sm absolute top-12 bottom-10 left-0 right-0 bg-custom-background-100/40 text-center">
|
||||||
<div className="rounded inline-flex mt-80 h-8 px-3 justify-center items-center bg-custom-background-80 text-custom-text-100 font-medium">
|
<div className="rounded inline-flex mt-80 h-8 px-3 justify-center items-center bg-custom-background-80 text-custom-text-100 font-medium">
|
||||||
{`This board is ordered by "${replaceUnderscoreIfSnakeCase(
|
{`This board is ordered by "${replaceUnderscoreIfSnakeCase(
|
||||||
@ -157,7 +164,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
)}"`}
|
)}"`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -192,6 +199,9 @@ export interface IKanBan {
|
|||||||
viewId?: string
|
viewId?: string
|
||||||
) => Promise<IIssue | undefined>;
|
) => Promise<IIssue | undefined>;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||||
@ -218,6 +228,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted,
|
isDragStarted,
|
||||||
quickAddCallback,
|
quickAddCallback,
|
||||||
viewId,
|
viewId,
|
||||||
|
disableIssueCreation,
|
||||||
|
isReadOnly,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { issueKanBanView: issueKanBanViewStore } = useMobxStore();
|
const { issueKanBanView: issueKanBanViewStore } = useMobxStore();
|
||||||
@ -246,6 +259,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -271,6 +287,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -296,6 +315,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -321,6 +343,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -346,6 +371,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -371,6 +399,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -396,6 +427,9 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,7 @@ import { HeaderGroupByCard } from "./group-by-card";
|
|||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar } from "@plane/ui";
|
import { Avatar } from "@plane/ui";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IAssigneesHeader {
|
export interface IAssigneesHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -15,6 +16,8 @@ export interface IAssigneesHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="base" />;
|
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="base" />;
|
||||||
@ -29,6 +32,8 @@ export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const assignee = column_value ?? null;
|
const assignee = column_value ?? null;
|
||||||
@ -56,6 +61,8 @@ export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ assignees: [assignee?.id] }}
|
issuePayload={{ assignees: [assignee?.id] }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -4,6 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
import { Icon } from "./assignee";
|
import { Icon } from "./assignee";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ICreatedByHeader {
|
export interface ICreatedByHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -14,6 +15,8 @@ export interface ICreatedByHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
||||||
@ -26,6 +29,8 @@ export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const createdBy = column_value ?? null;
|
const createdBy = column_value ?? null;
|
||||||
@ -53,6 +58,8 @@ export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ created_by: createdBy?.id }}
|
issuePayload={{ created_by: createdBy?.id }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -15,6 +15,7 @@ import useToast from "hooks/use-toast";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// types
|
// types
|
||||||
import { IIssue, ISearchIssueResponse } from "types";
|
import { IIssue, ISearchIssueResponse } from "types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
interface IHeaderGroupByCard {
|
interface IHeaderGroupByCard {
|
||||||
sub_group_by: string | null;
|
sub_group_by: string | null;
|
||||||
@ -26,13 +27,26 @@ interface IHeaderGroupByCard {
|
|||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
issuePayload: Partial<IIssue>;
|
issuePayload: Partial<IIssue>;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleService = new ModuleService();
|
const moduleService = new ModuleService();
|
||||||
const issueService = new IssueService();
|
const issueService = new IssueService();
|
||||||
|
|
||||||
export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
||||||
const { sub_group_by, column_id, icon, title, count, kanBanToggle, handleKanBanToggle, issuePayload } = props;
|
const {
|
||||||
|
sub_group_by,
|
||||||
|
column_id,
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
count,
|
||||||
|
kanBanToggle,
|
||||||
|
handleKanBanToggle,
|
||||||
|
issuePayload,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
|
} = props;
|
||||||
const verticalAlignPosition = kanBanToggle?.groupByHeaderMinMax.includes(column_id);
|
const verticalAlignPosition = kanBanToggle?.groupByHeaderMinMax.includes(column_id);
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
@ -84,7 +98,12 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CreateUpdateIssueModal isOpen={isOpen} handleClose={() => setIsOpen(false)} prePopulateData={issuePayload} />
|
<CreateUpdateIssueModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={() => setIsOpen(false)}
|
||||||
|
prePopulateData={issuePayload}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
{renderExistingIssueModal && (
|
{renderExistingIssueModal && (
|
||||||
<ExistingIssuesListModal
|
<ExistingIssuesListModal
|
||||||
isOpen={openExistingIssueListModal}
|
isOpen={openExistingIssueListModal}
|
||||||
@ -126,30 +145,31 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{renderExistingIssueModal ? (
|
{!disableIssueCreation &&
|
||||||
<CustomMenu
|
(renderExistingIssueModal ? (
|
||||||
width="auto"
|
<CustomMenu
|
||||||
customButton={
|
width="auto"
|
||||||
<span className="flex-shrink-0 w-[20px] h-[20px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
customButton={
|
||||||
<Plus height={14} width={14} strokeWidth={2} />
|
<span className="flex-shrink-0 w-[20px] h-[20px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
||||||
</span>
|
<Plus height={14} width={14} strokeWidth={2} />
|
||||||
}
|
</span>
|
||||||
>
|
}
|
||||||
<CustomMenu.MenuItem onClick={() => setIsOpen(true)}>
|
>
|
||||||
<span className="flex items-center justify-start gap-2">Create issue</span>
|
<CustomMenu.MenuItem onClick={() => setIsOpen(true)}>
|
||||||
</CustomMenu.MenuItem>
|
<span className="flex items-center justify-start gap-2">Create issue</span>
|
||||||
<CustomMenu.MenuItem onClick={() => setOpenExistingIssueListModal(true)}>
|
</CustomMenu.MenuItem>
|
||||||
<span className="flex items-center justify-start gap-2">Add an existing issue</span>
|
<CustomMenu.MenuItem onClick={() => setOpenExistingIssueListModal(true)}>
|
||||||
</CustomMenu.MenuItem>
|
<span className="flex items-center justify-start gap-2">Add an existing issue</span>
|
||||||
</CustomMenu>
|
</CustomMenu.MenuItem>
|
||||||
) : (
|
</CustomMenu>
|
||||||
<div
|
) : (
|
||||||
className="flex-shrink-0 w-[20px] h-[20px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all"
|
<div
|
||||||
onClick={() => setIsOpen(true)}
|
className="flex-shrink-0 w-[20px] h-[20px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all"
|
||||||
>
|
onClick={() => setIsOpen(true)}
|
||||||
<Plus width={14} strokeWidth={2} />
|
>
|
||||||
</div>
|
<Plus width={14} strokeWidth={2} />
|
||||||
)}
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,7 @@ import { LabelHeader } from "./label";
|
|||||||
import { CreatedByHeader } from "./created_by";
|
import { CreatedByHeader } from "./created_by";
|
||||||
// mobx
|
// mobx
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IKanBanGroupByHeaderRoot {
|
export interface IKanBanGroupByHeaderRoot {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -17,10 +18,22 @@ export interface IKanBanGroupByHeaderRoot {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = observer(
|
export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = observer(
|
||||||
({ column_id, column_value, sub_group_by, group_by, issues_count, kanBanToggle, handleKanBanToggle }) => (
|
({
|
||||||
|
column_id,
|
||||||
|
column_value,
|
||||||
|
sub_group_by,
|
||||||
|
group_by,
|
||||||
|
issues_count,
|
||||||
|
kanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
handleKanBanToggle,
|
||||||
|
currentStore,
|
||||||
|
}) => (
|
||||||
<>
|
<>
|
||||||
{group_by && group_by === "project" && (
|
{group_by && group_by === "project" && (
|
||||||
<ProjectHeader
|
<ProjectHeader
|
||||||
@ -32,6 +45,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -45,6 +60,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "state_detail.group" && (
|
{group_by && group_by === "state_detail.group" && (
|
||||||
@ -57,6 +74,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "priority" && (
|
{group_by && group_by === "priority" && (
|
||||||
@ -69,6 +88,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "labels" && (
|
{group_by && group_by === "labels" && (
|
||||||
@ -81,6 +102,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "assignees" && (
|
{group_by && group_by === "assignees" && (
|
||||||
@ -93,6 +116,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "created_by" && (
|
{group_by && group_by === "created_by" && (
|
||||||
@ -105,6 +130,8 @@ export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = obser
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ILabelHeader {
|
export interface ILabelHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -13,6 +14,8 @@ export interface ILabelHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ color }: any) => (
|
const Icon = ({ color }: any) => (
|
||||||
@ -29,6 +32,8 @@ export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const label = column_value ?? null;
|
const label = column_value ?? null;
|
||||||
@ -56,6 +61,8 @@ export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ labels: [label?.id] }}
|
issuePayload={{ labels: [label?.id] }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -7,6 +7,7 @@ import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
|||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { PriorityIcon } from "@plane/ui";
|
import { PriorityIcon } from "@plane/ui";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IPriorityHeader {
|
export interface IPriorityHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -17,6 +18,8 @@ export interface IPriorityHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
||||||
@ -29,6 +32,8 @@ export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const priority = column_value || null;
|
const priority = column_value || null;
|
||||||
@ -56,6 +61,8 @@ export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ priority: priority?.key }}
|
issuePayload={{ priority: priority?.key }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -5,6 +5,7 @@ import { HeaderGroupByCard } from "./group-by-card";
|
|||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
// emoji helper
|
// emoji helper
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IProjectHeader {
|
export interface IProjectHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -15,6 +16,8 @@ export interface IProjectHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ emoji }: any) => <div className="w-6 h-6">{renderEmoji(emoji)}</div>;
|
const Icon = ({ emoji }: any) => <div className="w-6 h-6">{renderEmoji(emoji)}</div>;
|
||||||
@ -29,6 +32,8 @@ export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const project = column_value ?? null;
|
const project = column_value ?? null;
|
||||||
@ -56,6 +61,8 @@ export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ project: project?.id }}
|
issuePayload={{ project: project?.id }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -4,6 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
import { StateGroupIcon } from "@plane/ui";
|
import { StateGroupIcon } from "@plane/ui";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IStateGroupHeader {
|
export interface IStateGroupHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -14,6 +15,8 @@ export interface IStateGroupHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => (
|
export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => (
|
||||||
@ -32,6 +35,8 @@ export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const stateGroup = column_value || null;
|
const stateGroup = column_value || null;
|
||||||
@ -59,6 +64,8 @@ export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{}}
|
issuePayload={{}}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -4,6 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
import { HeaderSubGroupByCard } from "./sub-group-by-card";
|
||||||
import { Icon } from "./state-group";
|
import { Icon } from "./state-group";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IStateHeader {
|
export interface IStateHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -14,6 +15,8 @@ export interface IStateHeader {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StateHeader: FC<IStateHeader> = observer((props) => {
|
export const StateHeader: FC<IStateHeader> = observer((props) => {
|
||||||
@ -26,6 +29,8 @@ export const StateHeader: FC<IStateHeader> = observer((props) => {
|
|||||||
issues_count,
|
issues_count,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const state = column_value ?? null;
|
const state = column_value ?? null;
|
||||||
@ -53,6 +58,8 @@ export const StateHeader: FC<IStateHeader> = observer((props) => {
|
|||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
issuePayload={{ state: state?.id }}
|
issuePayload={{ state: state?.id }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -7,6 +7,7 @@ import { AssigneesHeader } from "./assignee";
|
|||||||
import { PriorityHeader } from "./priority";
|
import { PriorityHeader } from "./priority";
|
||||||
import { LabelHeader } from "./label";
|
import { LabelHeader } from "./label";
|
||||||
import { CreatedByHeader } from "./created_by";
|
import { CreatedByHeader } from "./created_by";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IKanBanSubGroupByHeaderRoot {
|
export interface IKanBanSubGroupByHeaderRoot {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
@ -16,10 +17,22 @@ export interface IKanBanSubGroupByHeaderRoot {
|
|||||||
issues_count: number;
|
issues_count: number;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> = observer((props) => {
|
export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> = observer((props) => {
|
||||||
const { column_id, column_value, sub_group_by, group_by, issues_count, kanBanToggle, handleKanBanToggle } = props;
|
const {
|
||||||
|
column_id,
|
||||||
|
column_value,
|
||||||
|
sub_group_by,
|
||||||
|
group_by,
|
||||||
|
issues_count,
|
||||||
|
kanBanToggle,
|
||||||
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -33,6 +46,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sub_group_by && sub_group_by === "state_detail.group" && (
|
{sub_group_by && sub_group_by === "state_detail.group" && (
|
||||||
@ -45,6 +60,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sub_group_by && sub_group_by === "priority" && (
|
{sub_group_by && sub_group_by === "priority" && (
|
||||||
@ -57,6 +74,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sub_group_by && sub_group_by === "labels" && (
|
{sub_group_by && sub_group_by === "labels" && (
|
||||||
@ -69,6 +88,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sub_group_by && sub_group_by === "assignees" && (
|
{sub_group_by && sub_group_by === "assignees" && (
|
||||||
@ -81,6 +102,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{sub_group_by && sub_group_by === "created_by" && (
|
{sub_group_by && sub_group_by === "created_by" && (
|
||||||
@ -93,6 +116,8 @@ export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> =
|
|||||||
issues_count={issues_count}
|
issues_count={issues_count}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -19,10 +19,11 @@ export interface IKanBanProperties {
|
|||||||
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => void;
|
handleIssues: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => void;
|
||||||
displayProperties: IIssueDisplayProperties | null;
|
displayProperties: IIssueDisplayProperties | null;
|
||||||
showEmptyGroup: boolean;
|
showEmptyGroup: boolean;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) => {
|
export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) => {
|
||||||
const { sub_group_id, columnId: group_id, issue, handleIssues, displayProperties, showEmptyGroup } = props;
|
const { sub_group_id, columnId: group_id, issue, handleIssues, displayProperties, isReadOnly } = props;
|
||||||
|
|
||||||
const handleState = (state: IState) => {
|
const handleState = (state: IState) => {
|
||||||
handleIssues(
|
handleIssues(
|
||||||
@ -89,7 +90,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.state || null}
|
value={issue?.state || null}
|
||||||
onChange={handleState}
|
onChange={handleState}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -99,7 +100,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
<IssuePropertyPriority
|
<IssuePropertyPriority
|
||||||
value={issue?.priority || null}
|
value={issue?.priority || null}
|
||||||
onChange={handlePriority}
|
onChange={handlePriority}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -110,7 +111,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.labels || null}
|
value={issue?.labels || null}
|
||||||
onChange={handleLabel}
|
onChange={handleLabel}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -120,7 +121,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
<IssuePropertyDate
|
<IssuePropertyDate
|
||||||
value={issue?.start_date || null}
|
value={issue?.start_date || null}
|
||||||
onChange={(date: string) => handleStartDate(date)}
|
onChange={(date: string) => handleStartDate(date)}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
placeHolder="Start date"
|
placeHolder="Start date"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -130,7 +131,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
<IssuePropertyDate
|
<IssuePropertyDate
|
||||||
value={issue?.target_date || null}
|
value={issue?.target_date || null}
|
||||||
onChange={(date: string) => handleTargetDate(date)}
|
onChange={(date: string) => handleTargetDate(date)}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
placeHolder="Target date"
|
placeHolder="Target date"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -142,7 +143,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
value={issue?.assignees || null}
|
value={issue?.assignees || null}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
onChange={handleAssignee}
|
onChange={handleAssignee}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -153,7 +154,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
|||||||
projectId={issue?.project_detail?.id || null}
|
projectId={issue?.project_detail?.id || null}
|
||||||
value={issue?.estimate_point || null}
|
value={issue?.estimate_point || null}
|
||||||
onChange={handleEstimate}
|
onChange={handleEstimate}
|
||||||
disabled={false}
|
disabled={isReadOnly}
|
||||||
hideDropdownArrow
|
hideDropdownArrow
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -10,6 +10,7 @@ import { IIssue } from "types";
|
|||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
// components
|
// components
|
||||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ICycleKanBanLayout {}
|
export interface ICycleKanBanLayout {}
|
||||||
|
|
||||||
@ -18,7 +19,11 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { cycleIssues: cycleIssueStore, cycleIssueKanBanView: cycleIssueKanBanViewStore } = useMobxStore();
|
const {
|
||||||
|
cycleIssues: cycleIssueStore,
|
||||||
|
cycleIssuesFilter: cycleIssueFilterStore,
|
||||||
|
cycleIssueKanBanView: cycleIssueKanBanViewStore,
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
@ -39,10 +44,12 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
issueStore={cycleIssueStore}
|
issueStore={cycleIssueStore}
|
||||||
|
issuesFilterStore={cycleIssueFilterStore}
|
||||||
kanbanViewStore={cycleIssueKanBanViewStore}
|
kanbanViewStore={cycleIssueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={CycleIssueQuickActions}
|
QuickActions={CycleIssueQuickActions}
|
||||||
viewId={cycleId}
|
viewId={cycleId}
|
||||||
|
currentStore={EProjectStore.CYCLE}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { ProjectIssueQuickActions } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IIssue } from "types";
|
||||||
|
// constants
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
|
||||||
|
export interface IKanBanLayout {}
|
||||||
|
|
||||||
|
export const DraftKanBanLayout: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||||
|
|
||||||
|
const {
|
||||||
|
projectDraftIssues: issueStore,
|
||||||
|
projectDraftIssuesFilter: projectIssuesFilterStore,
|
||||||
|
issueKanBanView: issueKanBanViewStore,
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseKanBanRoot
|
||||||
|
issueActions={issueActions}
|
||||||
|
issuesFilterStore={projectIssuesFilterStore}
|
||||||
|
issueStore={issueStore}
|
||||||
|
kanbanViewStore={issueKanBanViewStore}
|
||||||
|
showLoader={true}
|
||||||
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
@ -10,6 +10,7 @@ import { IIssue } from "types";
|
|||||||
// constants
|
// constants
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IModuleKanBanLayout {}
|
export interface IModuleKanBanLayout {}
|
||||||
|
|
||||||
@ -20,8 +21,8 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
|||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
moduleIssues: moduleIssueStore,
|
moduleIssues: moduleIssueStore,
|
||||||
|
moduleIssuesFilter: moduleIssueFilterStore,
|
||||||
moduleIssueKanBanView: moduleIssueKanBanViewStore,
|
moduleIssueKanBanView: moduleIssueKanBanViewStore,
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
// const handleIssues = useCallback(
|
// const handleIssues = useCallback(
|
||||||
@ -58,17 +59,19 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
|||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||||
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, issue.id, moduleId, issue.bridge_id);
|
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
issueStore={moduleIssueStore}
|
issueStore={moduleIssueStore}
|
||||||
|
issuesFilterStore={moduleIssueFilterStore}
|
||||||
kanbanViewStore={moduleIssueKanBanViewStore}
|
kanbanViewStore={moduleIssueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={ModuleIssueQuickActions}
|
QuickActions={ModuleIssueQuickActions}
|
||||||
viewId={moduleId}
|
viewId={moduleId}
|
||||||
|
currentStore={EProjectStore.MODULE}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,166 +1,48 @@
|
|||||||
import { FC, useCallback, useState } from "react";
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { DragDropContext } from "@hello-pangea/dnd";
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { KanBanSwimLanes } from "../swimlanes";
|
|
||||||
import { KanBan } from "../default";
|
|
||||||
import { ProjectIssueQuickActions } from "components/issues";
|
import { ProjectIssueQuickActions } from "components/issues";
|
||||||
import { Spinner } from "@plane/ui";
|
|
||||||
// constants
|
|
||||||
import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
|
// constants
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IProfileIssuesKanBanLayout {}
|
export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, userId } = router.query as { workspaceSlug: string; userId: string };
|
||||||
|
|
||||||
export const ProfileIssuesKanBanLayout: FC = observer(() => {
|
|
||||||
const {
|
const {
|
||||||
workspace: workspaceStore,
|
workspaceProfileIssues: profileIssuesStore,
|
||||||
project: projectStore,
|
workspaceProfileIssuesFilter: profileIssueFiltersStore,
|
||||||
projectMember: { projectMembers },
|
|
||||||
projectState: projectStateStore,
|
|
||||||
profileIssues: profileIssuesStore,
|
|
||||||
profileIssueFilters: profileIssueFiltersStore,
|
|
||||||
issueKanBanView: issueKanBanViewStore,
|
issueKanBanView: issueKanBanViewStore,
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const issueActions = {
|
||||||
const { workspaceSlug } = router.query;
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !userId) return;
|
||||||
|
|
||||||
const issues = profileIssuesStore?.getIssues;
|
await profileIssuesStore.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||||
|
|
||||||
const sub_group_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.sub_group_by || null;
|
|
||||||
|
|
||||||
const group_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.group_by || null;
|
|
||||||
|
|
||||||
const order_by: string | null = profileIssueFiltersStore?.userDisplayFilters?.order_by || null;
|
|
||||||
|
|
||||||
const userDisplayFilters = profileIssueFiltersStore?.userDisplayFilters || null;
|
|
||||||
|
|
||||||
const displayProperties = profileIssueFiltersStore?.userDisplayProperties || null;
|
|
||||||
|
|
||||||
const currentKanBanView: "swimlanes" | "default" = profileIssueFiltersStore?.userDisplayFilters?.sub_group_by
|
|
||||||
? "swimlanes"
|
|
||||||
: "default";
|
|
||||||
|
|
||||||
const [isDragStarted, setIsDragStarted] = useState<boolean>(false);
|
|
||||||
|
|
||||||
// const onDragStart = () => {
|
|
||||||
// setIsDragStarted(true);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const onDragEnd = (result: any) => {
|
|
||||||
setIsDragStarted(false);
|
|
||||||
if (!result) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
result.destination &&
|
|
||||||
result.source &&
|
|
||||||
result.destination.droppableId === result.source.droppableId &&
|
|
||||||
result.destination.index === result.source.index
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
currentKanBanView === "default"
|
|
||||||
? issueKanBanViewStore?.handleDragDrop(result.source, result.destination)
|
|
||||||
: issueKanBanViewStore?.handleSwimlaneDragDrop(result.source, result.destination);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIssues = useCallback(
|
|
||||||
(sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
|
|
||||||
if (action === EIssueActions.UPDATE) {
|
|
||||||
profileIssuesStore.updateIssueStructure(group_by, sub_group_by, issue);
|
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
|
||||||
}
|
|
||||||
if (action === EIssueActions.DELETE) profileIssuesStore.deleteIssue(group_by, sub_group_by, issue);
|
|
||||||
},
|
},
|
||||||
[profileIssuesStore, issueDetailStore, workspaceSlug]
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
);
|
if (!workspaceSlug || !userId) return;
|
||||||
|
|
||||||
const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {
|
await profileIssuesStore.removeIssue(workspaceSlug, userId, issue.project, issue.id);
|
||||||
issueKanBanViewStore.handleKanBanToggle(toggle, value);
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const states = projectStateStore?.projectStates || null;
|
|
||||||
const priorities = ISSUE_PRIORITIES || null;
|
|
||||||
const labels = workspaceStore.workspaceLabels || null;
|
|
||||||
const stateGroups = ISSUE_STATE_GROUPS || null;
|
|
||||||
const projects = projectStore?.workspaceProjects || null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<BaseKanBanRoot
|
||||||
{profileIssuesStore.loader ? (
|
issueActions={issueActions}
|
||||||
<div className="w-full h-full flex justify-center items-center">
|
issuesFilterStore={profileIssueFiltersStore}
|
||||||
<Spinner />
|
issueStore={profileIssuesStore}
|
||||||
</div>
|
kanbanViewStore={issueKanBanViewStore}
|
||||||
) : (
|
showLoader={true}
|
||||||
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
|
QuickActions={ProjectIssueQuickActions}
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
currentStore={EProjectStore.PROFILE}
|
||||||
{currentKanBanView === "default" ? (
|
/>
|
||||||
<KanBan
|
|
||||||
issues={{}}
|
|
||||||
issueIds={[]}
|
|
||||||
sub_group_by={sub_group_by}
|
|
||||||
group_by={group_by}
|
|
||||||
order_by={order_by}
|
|
||||||
handleIssues={handleIssues}
|
|
||||||
quickActions={(sub_group_by, group_by, issue) => (
|
|
||||||
<ProjectIssueQuickActions
|
|
||||||
issue={issue}
|
|
||||||
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, EIssueActions.DELETE)}
|
|
||||||
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, EIssueActions.UPDATE)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
|
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
|
||||||
states={states}
|
|
||||||
stateGroups={stateGroups}
|
|
||||||
priorities={priorities}
|
|
||||||
labels={labels}
|
|
||||||
members={projectMembers?.map((m) => m.member) ?? null}
|
|
||||||
projects={projects}
|
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
|
||||||
isDragStarted={isDragStarted}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<KanBanSwimLanes
|
|
||||||
issues={{}}
|
|
||||||
issueIds={[]}
|
|
||||||
sub_group_by={sub_group_by}
|
|
||||||
group_by={group_by}
|
|
||||||
order_by={order_by}
|
|
||||||
handleIssues={handleIssues}
|
|
||||||
quickActions={(sub_group_by, group_by, issue) => (
|
|
||||||
<ProjectIssueQuickActions
|
|
||||||
issue={issue}
|
|
||||||
handleDelete={async () => handleIssues(sub_group_by, group_by, issue, EIssueActions.DELETE)}
|
|
||||||
handleUpdate={async (data) => handleIssues(sub_group_by, group_by, data, EIssueActions.UPDATE)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
displayProperties={displayProperties}
|
|
||||||
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
|
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
|
||||||
states={states}
|
|
||||||
stateGroups={stateGroups}
|
|
||||||
priorities={priorities}
|
|
||||||
labels={labels}
|
|
||||||
members={projectMembers?.map((m) => m.member) ?? null}
|
|
||||||
projects={projects}
|
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
|
||||||
isDragStarted={isDragStarted}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DragDropContext>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ import { IIssue } from "types";
|
|||||||
// constants
|
// constants
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IKanBanLayout {}
|
export interface IKanBanLayout {}
|
||||||
|
|
||||||
@ -18,30 +19,32 @@ export const KanBanLayout: React.FC = observer(() => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
projectIssues: issueStore,
|
projectIssues: issueStore,
|
||||||
|
projectIssuesFilter: issuesFilterStore,
|
||||||
issueKanBanView: issueKanBanViewStore,
|
issueKanBanView: issueKanBanViewStore,
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueDetailStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
await issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueDetailStore.deleteIssue(workspaceSlug, issue.project, issue.id);
|
await issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
issuesFilterStore={issuesFilterStore}
|
||||||
issueStore={issueStore}
|
issueStore={issueStore}
|
||||||
kanbanViewStore={issueKanBanViewStore}
|
kanbanViewStore={issueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
currentStore={EProjectStore.PROJECT}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ import { EIssueActions } from "../../types";
|
|||||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
// components
|
// components
|
||||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IViewKanBanLayout {}
|
export interface IViewKanBanLayout {}
|
||||||
|
|
||||||
@ -18,30 +19,32 @@ export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
viewIssues: projectViewIssuesStore,
|
viewIssues: projectViewIssuesStore,
|
||||||
|
viewIssuesFilter: projectIssueViewFiltersStore,
|
||||||
issueKanBanView: projectViewIssueKanBanViewStore,
|
issueKanBanView: projectViewIssueKanBanViewStore,
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueDetailStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
await projectViewIssuesStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueDetailStore.deleteIssue(workspaceSlug, issue.project, issue.id);
|
await projectViewIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
|
issuesFilterStore={projectIssueViewFiltersStore}
|
||||||
issueStore={projectViewIssuesStore}
|
issueStore={projectViewIssuesStore}
|
||||||
kanbanViewStore={projectViewIssueKanBanViewStore}
|
kanbanViewStore={projectViewIssueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
currentStore={EProjectStore.PROJECT_VIEW}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ import { IIssueResponse, IGroupedIssues, ISubGroupedIssues, TUnGroupedIssues } f
|
|||||||
// constants
|
// constants
|
||||||
import { getValueFromObject } from "constants/issue";
|
import { getValueFromObject } from "constants/issue";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
interface ISubGroupSwimlaneHeader {
|
interface ISubGroupSwimlaneHeader {
|
||||||
issues: IIssueResponse;
|
issues: IIssueResponse;
|
||||||
@ -20,9 +21,10 @@ interface ISubGroupSwimlaneHeader {
|
|||||||
listKey: string;
|
listKey: string;
|
||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
||||||
issues,
|
|
||||||
issueIds,
|
issueIds,
|
||||||
sub_group_by,
|
sub_group_by,
|
||||||
group_by,
|
group_by,
|
||||||
@ -30,6 +32,8 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
|||||||
listKey,
|
listKey,
|
||||||
kanBanToggle,
|
kanBanToggle,
|
||||||
handleKanBanToggle,
|
handleKanBanToggle,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
}) => {
|
}) => {
|
||||||
const calculateIssueCount = (column_id: string) => {
|
const calculateIssueCount = (column_id: string) => {
|
||||||
let issueCount = 0;
|
let issueCount = 0;
|
||||||
@ -54,6 +58,8 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
|||||||
issues_count={calculateIssueCount(getValueFromObject(_list, listKey) as string)}
|
issues_count={calculateIssueCount(getValueFromObject(_list, listKey) as string)}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -78,6 +84,10 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
|||||||
kanBanToggle: any;
|
kanBanToggle: any;
|
||||||
handleKanBanToggle: any;
|
handleKanBanToggle: any;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
|
enableQuickIssueCreate: boolean;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||||
const {
|
const {
|
||||||
@ -101,6 +111,9 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
members,
|
members,
|
||||||
projects,
|
projects,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
|
disableIssueCreation,
|
||||||
|
enableQuickIssueCreate,
|
||||||
|
isReadOnly,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const calculateIssueCount = (column_id: string) => {
|
const calculateIssueCount = (column_id: string) => {
|
||||||
@ -128,6 +141,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
issues_count={calculateIssueCount(getValueFromObject(_list, listKey) as string)}
|
issues_count={calculateIssueCount(getValueFromObject(_list, listKey) as string)}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full border-b border-custom-border-400 border-dashed" />
|
<div className="w-full border-b border-custom-border-400 border-dashed" />
|
||||||
@ -153,8 +167,9 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
|||||||
labels={labels}
|
labels={labels}
|
||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
enableQuickIssueCreate
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -183,6 +198,10 @@ export interface IKanBanSwimLanes {
|
|||||||
members: IUserLite[] | null;
|
members: IUserLite[] | null;
|
||||||
projects: IProject[] | null;
|
projects: IProject[] | null;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
|
enableQuickIssueCreate: boolean;
|
||||||
|
isReadOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||||
@ -205,6 +224,10 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members,
|
members,
|
||||||
projects,
|
projects,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
|
disableIssueCreation,
|
||||||
|
enableQuickIssueCreate,
|
||||||
|
isReadOnly,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -220,6 +243,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`id`}
|
listKey={`id`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -233,6 +258,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`id`}
|
listKey={`id`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -246,6 +273,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`key`}
|
listKey={`key`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -259,6 +288,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`key`}
|
listKey={`key`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -272,6 +302,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`id`}
|
listKey={`id`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -285,6 +317,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`id`}
|
listKey={`id`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -298,6 +332,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
listKey={`id`}
|
listKey={`id`}
|
||||||
kanBanToggle={kanBanToggle}
|
kanBanToggle={kanBanToggle}
|
||||||
handleKanBanToggle={handleKanBanToggle}
|
handleKanBanToggle={handleKanBanToggle}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -324,6 +360,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -349,6 +388,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -374,6 +416,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -399,6 +444,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -424,6 +472,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -449,6 +500,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -474,6 +528,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -499,6 +556,9 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
members={members}
|
members={members}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||||
|
isReadOnly={isReadOnly}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,10 @@ import {
|
|||||||
ICycleIssuesStore,
|
ICycleIssuesStore,
|
||||||
IModuleIssuesFilterStore,
|
IModuleIssuesFilterStore,
|
||||||
IModuleIssuesStore,
|
IModuleIssuesStore,
|
||||||
|
IProfileIssuesFilterStore,
|
||||||
|
IProfileIssuesStore,
|
||||||
|
IProjectArchivedIssuesStore,
|
||||||
|
IProjectDraftIssuesStore,
|
||||||
IProjectIssuesFilterStore,
|
IProjectIssuesFilterStore,
|
||||||
IProjectIssuesStore,
|
IProjectIssuesStore,
|
||||||
IViewIssuesFilterStore,
|
IViewIssuesFilterStore,
|
||||||
@ -18,6 +22,7 @@ import {
|
|||||||
} from "store/issues";
|
} from "store/issues";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { IIssueResponse } from "store/issues/types";
|
import { IIssueResponse } from "store/issues/types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
enum EIssueActions {
|
enum EIssueActions {
|
||||||
UPDATE = "update",
|
UPDATE = "update",
|
||||||
@ -30,8 +35,16 @@ interface IBaseListRoot {
|
|||||||
| IProjectIssuesFilterStore
|
| IProjectIssuesFilterStore
|
||||||
| IModuleIssuesFilterStore
|
| IModuleIssuesFilterStore
|
||||||
| ICycleIssuesFilterStore
|
| ICycleIssuesFilterStore
|
||||||
| IViewIssuesFilterStore;
|
| IViewIssuesFilterStore
|
||||||
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
| IProfileIssuesFilterStore;
|
||||||
|
issueStore:
|
||||||
|
| IProjectIssuesStore
|
||||||
|
| IModuleIssuesStore
|
||||||
|
| ICycleIssuesStore
|
||||||
|
| IViewIssuesStore
|
||||||
|
| IProjectArchivedIssuesStore
|
||||||
|
| IProjectDraftIssuesStore
|
||||||
|
| IProfileIssuesStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => void;
|
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => void;
|
||||||
@ -40,10 +53,11 @@ interface IBaseListRoot {
|
|||||||
};
|
};
|
||||||
getProjects: (projectStore: IProjectStore) => IProject[] | null;
|
getProjects: (projectStore: IProjectStore) => IProject[] | null;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BaseListRoot = observer((props: IBaseListRoot) => {
|
export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||||
const { issueFilterStore, issueStore, QuickActions, issueActions, getProjects, viewId } = props;
|
const { issueFilterStore, issueStore, QuickActions, issueActions, getProjects, viewId, currentStore } = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
@ -52,8 +66,10 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||||||
projectLabel: { projectLabels },
|
projectLabel: { projectLabels },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueIds = issueStore.getIssuesIds || [];
|
const issueIds = issueStore?.getIssuesIds || [];
|
||||||
const issues = issueStore.getIssues;
|
const issues = issueStore?.getIssues;
|
||||||
|
|
||||||
|
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
||||||
|
|
||||||
const displayFilters = issueFilterStore?.issueFilters?.displayFilters;
|
const displayFilters = issueFilterStore?.issueFilters?.displayFilters;
|
||||||
const group_by = displayFilters?.group_by || null;
|
const group_by = displayFilters?.group_by || null;
|
||||||
@ -75,7 +91,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{issueStore.loader === "mutation" ? (
|
{issueStore?.loader === "init-loader" ? (
|
||||||
<div className="w-full h-full flex justify-center items-center">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
@ -108,10 +124,12 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||||||
projects={projects}
|
projects={projects}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
showEmptyGroup={showEmptyGroup}
|
showEmptyGroup={showEmptyGroup}
|
||||||
enableIssueQuickAdd={true}
|
|
||||||
isReadonly={false}
|
|
||||||
quickAddCallback={issueStore.quickAddIssue}
|
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
quickAddCallback={issueStore?.quickAddIssue}
|
||||||
|
enableIssueQuickAdd={!!enableQuickAdd}
|
||||||
|
isReadonly={!enableInlineEditing}
|
||||||
|
disableIssueCreation={!enableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -4,10 +4,11 @@ import { ListGroupByHeaderRoot } from "./headers/group-by-root";
|
|||||||
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types";
|
import { IIssue, IIssueDisplayProperties, IIssueLabel, IProject, IState, IUserLite } from "types";
|
||||||
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues } from "store/issues/types";
|
import { IIssueResponse, IGroupedIssues, TUnGroupedIssues, ViewFlags } from "store/issues/types";
|
||||||
import { EIssueActions } from "../types";
|
import { EIssueActions } from "../types";
|
||||||
// constants
|
// constants
|
||||||
import { getValueFromObject } from "constants/issue";
|
import { getValueFromObject } from "constants/issue";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IGroupByList {
|
export interface IGroupByList {
|
||||||
issueIds: IGroupedIssues | TUnGroupedIssues | any;
|
issueIds: IGroupedIssues | TUnGroupedIssues | any;
|
||||||
@ -29,6 +30,8 @@ export interface IGroupByList {
|
|||||||
data: IIssue,
|
data: IIssue,
|
||||||
viewId?: string
|
viewId?: string
|
||||||
) => Promise<IIssue | undefined>;
|
) => Promise<IIssue | undefined>;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +52,8 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||||||
isReadonly,
|
isReadonly,
|
||||||
quickAddCallback,
|
quickAddCallback,
|
||||||
viewId,
|
viewId,
|
||||||
|
disableIssueCreation,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const prePopulateQuickAddData = (groupByKey: string | null, value: any) => {
|
const prePopulateQuickAddData = (groupByKey: string | null, value: any) => {
|
||||||
@ -84,6 +89,8 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||||||
? issueIds?.length || 0
|
? issueIds?.length || 0
|
||||||
: issueIds?.[getValueFromObject(_list, listKey) as string]?.length || 0
|
: issueIds?.[getValueFromObject(_list, listKey) as string]?.length || 0
|
||||||
}
|
}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -138,6 +145,8 @@ export interface IList {
|
|||||||
viewId?: string
|
viewId?: string
|
||||||
) => Promise<IIssue | undefined>;
|
) => Promise<IIssue | undefined>;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const List: React.FC<IList> = (props) => {
|
export const List: React.FC<IList> = (props) => {
|
||||||
@ -153,13 +162,14 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
showEmptyGroup,
|
showEmptyGroup,
|
||||||
enableIssueQuickAdd,
|
enableIssueQuickAdd,
|
||||||
isReadonly,
|
isReadonly,
|
||||||
|
disableIssueCreation,
|
||||||
states,
|
states,
|
||||||
stateGroups,
|
stateGroups,
|
||||||
priorities,
|
priorities,
|
||||||
labels,
|
labels,
|
||||||
members,
|
members,
|
||||||
projects,
|
projects,
|
||||||
|
currentStore,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -181,6 +191,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -200,6 +212,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -219,6 +233,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -238,6 +254,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -257,6 +275,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -276,6 +296,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -295,6 +317,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -314,6 +338,8 @@ export const List: React.FC<IList> = (props) => {
|
|||||||
isReadonly={isReadonly}
|
isReadonly={isReadonly}
|
||||||
quickAddCallback={quickAddCallback}
|
quickAddCallback={quickAddCallback}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,17 +4,20 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar } from "@plane/ui";
|
import { Avatar } from "@plane/ui";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IAssigneesHeader {
|
export interface IAssigneesHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="md" />;
|
export const Icon = ({ user }: any) => <Avatar name={user.display_name} src={user.avatar} size="md" />;
|
||||||
|
|
||||||
export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const assignee = column_value ?? null;
|
const assignee = column_value ?? null;
|
||||||
|
|
||||||
@ -26,6 +29,8 @@ export const AssigneesHeader: FC<IAssigneesHeader> = observer((props) => {
|
|||||||
title={assignee?.display_name || ""}
|
title={assignee?.display_name || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ assignees: [assignee?.member?.id] }}
|
issuePayload={{ assignees: [assignee?.member?.id] }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -3,15 +3,18 @@ import { observer } from "mobx-react-lite";
|
|||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { Icon } from "./assignee";
|
import { Icon } from "./assignee";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ICreatedByHeader {
|
export interface ICreatedByHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const createdBy = column_value ?? null;
|
const createdBy = column_value ?? null;
|
||||||
|
|
||||||
@ -23,6 +26,8 @@ export const CreatedByHeader: FC<ICreatedByHeader> = observer((props) => {
|
|||||||
title={createdBy?.display_name || ""}
|
title={createdBy?.display_name || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ created_by: createdBy?.member?.id }}
|
issuePayload={{ created_by: createdBy?.member?.id }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -1,15 +1,26 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IEmptyHeader {
|
export interface IEmptyHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EmptyHeader: React.FC<IEmptyHeader> = observer((props) => {
|
export const EmptyHeader: React.FC<IEmptyHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_id, column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
return <HeaderGroupByCard title={column_value?.title || "All Issues"} count={issues_count} issuePayload={{}} />;
|
return (
|
||||||
|
<HeaderGroupByCard
|
||||||
|
title={column_value?.title || "All Issues"}
|
||||||
|
count={issues_count}
|
||||||
|
issuePayload={{}}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -6,125 +6,124 @@ import { CircleDashed, Plus } from "lucide-react";
|
|||||||
import { CreateUpdateIssueModal } from "components/issues/modal";
|
import { CreateUpdateIssueModal } from "components/issues/modal";
|
||||||
import { ExistingIssuesListModal } from "components/core";
|
import { ExistingIssuesListModal } from "components/core";
|
||||||
import { CustomMenu } from "@plane/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
// hooks
|
|
||||||
import useToast from "hooks/use-toast";
|
|
||||||
// mobx
|
// mobx
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// types
|
// types
|
||||||
import { IIssue, ISearchIssueResponse } from "types";
|
import { IIssue, ISearchIssueResponse } from "types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
interface IHeaderGroupByCard {
|
interface IHeaderGroupByCard {
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
title: string;
|
title: string;
|
||||||
count: number;
|
count: number;
|
||||||
issuePayload: Partial<IIssue>;
|
issuePayload: Partial<IIssue>;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HeaderGroupByCard = observer(({ icon, title, count, issuePayload }: IHeaderGroupByCard) => {
|
export const HeaderGroupByCard = observer(
|
||||||
const router = useRouter();
|
({ icon, title, count, issuePayload, disableIssueCreation, currentStore }: IHeaderGroupByCard) => {
|
||||||
const { workspaceSlug, projectId, moduleId, cycleId } = router.query;
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId, moduleId, cycleId } = router.query;
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [openExistingIssueListModal, setOpenExistingIssueListModal] = React.useState(false);
|
||||||
|
|
||||||
const [openExistingIssueListModal, setOpenExistingIssueListModal] = React.useState(false);
|
const renderExistingIssueModal = moduleId || cycleId;
|
||||||
|
const ExistingIssuesListModalPayload = moduleId ? { module: true } : { cycle: true };
|
||||||
|
|
||||||
const renderExistingIssueModal = moduleId || cycleId;
|
const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => {
|
||||||
const ExistingIssuesListModalPayload = moduleId ? { module: true } : { cycle: true };
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => {
|
const payload = {
|
||||||
if (!workspaceSlug || !projectId) return;
|
issues: data.map((i) => i.id),
|
||||||
|
};
|
||||||
|
|
||||||
const payload = {
|
// await moduleService
|
||||||
issues: data.map((i) => i.id),
|
// .addIssuesToModule(workspaceSlug as string, projectId as string, moduleId as string, payload, user)
|
||||||
|
// .catch(() =>
|
||||||
|
// setToastAlert({
|
||||||
|
// type: "error",
|
||||||
|
// title: "Error!",
|
||||||
|
// message: "Selected issues could not be added to the module. Please try again.",
|
||||||
|
// })
|
||||||
|
// );
|
||||||
};
|
};
|
||||||
|
|
||||||
// await moduleService
|
const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => {
|
||||||
// .addIssuesToModule(workspaceSlug as string, projectId as string, moduleId as string, payload, user)
|
if (!workspaceSlug || !projectId) return;
|
||||||
// .catch(() =>
|
|
||||||
// setToastAlert({
|
|
||||||
// type: "error",
|
|
||||||
// title: "Error!",
|
|
||||||
// message: "Selected issues could not be added to the module. Please try again.",
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => {
|
const payload = {
|
||||||
if (!workspaceSlug || !projectId) return;
|
issues: data.map((i) => i.id),
|
||||||
|
};
|
||||||
|
|
||||||
const payload = {
|
// await issueService
|
||||||
issues: data.map((i) => i.id),
|
// .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, payload, user)
|
||||||
|
// .catch(() => {
|
||||||
|
// setToastAlert({
|
||||||
|
// type: "error",
|
||||||
|
// title: "Error!",
|
||||||
|
// message: "Selected issues could not be added to the cycle. Please try again.",
|
||||||
|
// });
|
||||||
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
// await issueService
|
return (
|
||||||
// .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, payload, user)
|
<>
|
||||||
// .catch(() => {
|
<div className="flex-shrink-0 relative flex gap-2 py-1.5 flex-row items-center w-full">
|
||||||
// setToastAlert({
|
<div className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center">
|
||||||
// type: "error",
|
{icon ? icon : <CircleDashed className="h-3.5 w-3.5" strokeWidth={2} />}
|
||||||
// title: "Error!",
|
|
||||||
// message: "Selected issues could not be added to the cycle. Please try again.",
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="flex-shrink-0 relative flex gap-2 py-1.5 flex-row items-center w-full">
|
|
||||||
<div className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center">
|
|
||||||
{icon ? icon : <CircleDashed className="h-3.5 w-3.5" strokeWidth={2} />}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-1 flex-row w-full">
|
|
||||||
<div className="font-medium line-clamp-1 text-custom-text-100">{title}</div>
|
|
||||||
<div className="text-sm font-medium text-custom-text-300 pl-2">{count || 0}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{renderExistingIssueModal ? (
|
|
||||||
<CustomMenu
|
|
||||||
width="auto"
|
|
||||||
customButton={
|
|
||||||
<span className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
|
||||||
<Plus className="h-3.5 w-3.5" strokeWidth={2} />
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<CustomMenu.MenuItem onClick={() => setIsOpen(true)}>
|
|
||||||
<span className="flex items-center justify-start gap-2">Create issue</span>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
<CustomMenu.MenuItem onClick={() => setOpenExistingIssueListModal(true)}>
|
|
||||||
<span className="flex items-center justify-start gap-2">Add an existing issue</span>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
</CustomMenu>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all"
|
|
||||||
onClick={() => setIsOpen(true)}
|
|
||||||
>
|
|
||||||
<Plus width={14} strokeWidth={2} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<CreateUpdateIssueModal
|
<div className="flex items-center gap-1 flex-row w-full">
|
||||||
isOpen={isOpen}
|
<div className="font-medium line-clamp-1 text-custom-text-100">{title}</div>
|
||||||
handleClose={() => setIsOpen(false)}
|
<div className="text-sm font-medium text-custom-text-300 pl-2">{count || 0}</div>
|
||||||
handleSubmit={(data: Partial<IIssue>) => {
|
</div>
|
||||||
console.log(data);
|
|
||||||
return Promise.resolve();
|
|
||||||
}}
|
|
||||||
prePopulateData={issuePayload}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{renderExistingIssueModal && (
|
{!disableIssueCreation &&
|
||||||
<ExistingIssuesListModal
|
(renderExistingIssueModal ? (
|
||||||
isOpen={openExistingIssueListModal}
|
<CustomMenu
|
||||||
handleClose={() => setOpenExistingIssueListModal(false)}
|
width="auto"
|
||||||
searchParams={ExistingIssuesListModalPayload}
|
customButton={
|
||||||
handleOnSubmit={moduleId ? handleAddIssuesToModule : handleAddIssuesToCycle}
|
<span className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
||||||
|
<Plus className="h-3.5 w-3.5" strokeWidth={2} />
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CustomMenu.MenuItem onClick={() => setIsOpen(true)}>
|
||||||
|
<span className="flex items-center justify-start gap-2">Create issue</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
<CustomMenu.MenuItem onClick={() => setOpenExistingIssueListModal(true)}>
|
||||||
|
<span className="flex items-center justify-start gap-2">Add an existing issue</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
</CustomMenu>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="flex-shrink-0 w-5 h-5 rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all"
|
||||||
|
onClick={() => setIsOpen(true)}
|
||||||
|
>
|
||||||
|
<Plus width={14} strokeWidth={2} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<CreateUpdateIssueModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={() => setIsOpen(false)}
|
||||||
|
currentStore={currentStore}
|
||||||
|
prePopulateData={issuePayload}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</div>
|
{renderExistingIssueModal && (
|
||||||
</>
|
<ExistingIssuesListModal
|
||||||
);
|
isOpen={openExistingIssueListModal}
|
||||||
});
|
handleClose={() => setOpenExistingIssueListModal(false)}
|
||||||
|
searchParams={ExistingIssuesListModalPayload}
|
||||||
|
handleOnSubmit={moduleId ? handleAddIssuesToModule : handleAddIssuesToCycle}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
@ -9,43 +9,94 @@ import { LabelHeader } from "./label";
|
|||||||
import { CreatedByHeader } from "./created-by";
|
import { CreatedByHeader } from "./created-by";
|
||||||
// mobx
|
// mobx
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IListGroupByHeaderRoot {
|
export interface IListGroupByHeaderRoot {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
group_by: string | null;
|
group_by: string | null;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ListGroupByHeaderRoot: React.FC<IListGroupByHeaderRoot> = observer((props) => {
|
export const ListGroupByHeaderRoot: React.FC<IListGroupByHeaderRoot> = observer((props) => {
|
||||||
const { column_id, column_value, group_by, issues_count } = props;
|
const { column_id, column_value, group_by, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!group_by && group_by === null && (
|
{!group_by && group_by === null && (
|
||||||
<EmptyHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<EmptyHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "project" && (
|
{group_by && group_by === "project" && (
|
||||||
<ProjectHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<ProjectHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{group_by && group_by === "state" && (
|
{group_by && group_by === "state" && (
|
||||||
<StateHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<StateHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "state_detail.group" && (
|
{group_by && group_by === "state_detail.group" && (
|
||||||
<StateGroupHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<StateGroupHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "priority" && (
|
{group_by && group_by === "priority" && (
|
||||||
<PriorityHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<PriorityHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "labels" && (
|
{group_by && group_by === "labels" && (
|
||||||
<LabelHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<LabelHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "assignees" && (
|
{group_by && group_by === "assignees" && (
|
||||||
<AssigneesHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<AssigneesHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{group_by && group_by === "created_by" && (
|
{group_by && group_by === "created_by" && (
|
||||||
<CreatedByHeader column_id={column_id} column_value={column_value} issues_count={issues_count} />
|
<CreatedByHeader
|
||||||
|
column_id={column_id}
|
||||||
|
column_value={column_value}
|
||||||
|
issues_count={issues_count}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -2,11 +2,14 @@ import { FC } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ILabelHeader {
|
export interface ILabelHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ color }: any) => (
|
const Icon = ({ color }: any) => (
|
||||||
@ -14,7 +17,7 @@ const Icon = ({ color }: any) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const label = column_value ?? null;
|
const label = column_value ?? null;
|
||||||
|
|
||||||
@ -26,6 +29,8 @@ export const LabelHeader: FC<ILabelHeader> = observer((props) => {
|
|||||||
title={column_value?.name || ""}
|
title={column_value?.name || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ labels: [label.id] }}
|
issuePayload={{ labels: [label.id] }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -3,11 +3,14 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { AlertCircle, SignalHigh, SignalMedium, SignalLow, Ban } from "lucide-react";
|
import { AlertCircle, SignalHigh, SignalMedium, SignalLow, Ban } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IPriorityHeader {
|
export interface IPriorityHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ priority }: any) => (
|
const Icon = ({ priority }: any) => (
|
||||||
@ -37,7 +40,7 @@ const Icon = ({ priority }: any) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_id, column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const priority = column_value ?? null;
|
const priority = column_value ?? null;
|
||||||
|
|
||||||
@ -49,6 +52,8 @@ export const PriorityHeader: FC<IPriorityHeader> = observer((props) => {
|
|||||||
title={priority?.title || ""}
|
title={priority?.title || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ priority: priority?.key }}
|
issuePayload={{ priority: priority?.key }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -4,17 +4,20 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
// emoji helper
|
// emoji helper
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IProjectHeader {
|
export interface IProjectHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = ({ emoji }: any) => <div className="w-6 h-6">{renderEmoji(emoji)}</div>;
|
const Icon = ({ emoji }: any) => <div className="w-6 h-6">{renderEmoji(emoji)}</div>;
|
||||||
|
|
||||||
export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const project = column_value ?? null;
|
const project = column_value ?? null;
|
||||||
|
|
||||||
@ -26,6 +29,8 @@ export const ProjectHeader: FC<IProjectHeader> = observer((props) => {
|
|||||||
title={project?.name || ""}
|
title={project?.name || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ project: project?.id ?? "" }}
|
issuePayload={{ project: project?.id ?? "" }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -6,11 +6,14 @@ import { HeaderGroupByCard } from "./group-by-card";
|
|||||||
import { StateGroupIcon } from "@plane/ui";
|
import { StateGroupIcon } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
import { capitalizeFirstLetter } from "helpers/string.helper";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IStateGroupHeader {
|
export interface IStateGroupHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => (
|
export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) => (
|
||||||
@ -20,7 +23,7 @@ export const Icon = ({ stateGroup, color }: { stateGroup: any; color?: any }) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const stateGroup = column_value ?? null;
|
const stateGroup = column_value ?? null;
|
||||||
|
|
||||||
@ -32,6 +35,8 @@ export const StateGroupHeader: FC<IStateGroupHeader> = observer((props) => {
|
|||||||
title={capitalizeFirstLetter(stateGroup?.key) || ""}
|
title={capitalizeFirstLetter(stateGroup?.key) || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{}}
|
issuePayload={{}}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -3,15 +3,18 @@ import { observer } from "mobx-react-lite";
|
|||||||
// components
|
// components
|
||||||
import { HeaderGroupByCard } from "./group-by-card";
|
import { HeaderGroupByCard } from "./group-by-card";
|
||||||
import { Icon } from "./state-group";
|
import { Icon } from "./state-group";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IStateHeader {
|
export interface IStateHeader {
|
||||||
column_id: string;
|
column_id: string;
|
||||||
column_value: any;
|
column_value: any;
|
||||||
issues_count: number;
|
issues_count: number;
|
||||||
|
disableIssueCreation?: boolean;
|
||||||
|
currentStore: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const StateHeader: FC<IStateHeader> = observer((props) => {
|
export const StateHeader: FC<IStateHeader> = observer((props) => {
|
||||||
const { column_id, column_value, issues_count } = props;
|
const { column_value, issues_count, disableIssueCreation, currentStore } = props;
|
||||||
|
|
||||||
const state = column_value ?? null;
|
const state = column_value ?? null;
|
||||||
|
|
||||||
@ -23,6 +26,8 @@ export const StateHeader: FC<IStateHeader> = observer((props) => {
|
|||||||
title={state?.name || ""}
|
title={state?.name || ""}
|
||||||
count={issues_count}
|
count={issues_count}
|
||||||
issuePayload={{ state: state?.id }}
|
issuePayload={{ state: state?.id }}
|
||||||
|
disableIssueCreation={disableIssueCreation}
|
||||||
|
currentStore={currentStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -11,18 +11,20 @@ import { IIssue } from "types";
|
|||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
import { IProjectStore } from "store/project";
|
import { IProjectStore } from "store/project";
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ArchivedIssueListLayout: FC = observer(() => {
|
export const ArchivedIssueListLayout: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const { archivedIssues: archivedIssueStore, archivedIssueFilters: archivedIssueFiltersStore } = useMobxStore();
|
const { projectArchivedIssues: archivedIssueStore, projectArchivedIssuesFilter: archivedIssueFiltersStore } =
|
||||||
|
useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
archivedIssueStore.deleteArchivedIssue(group_by, null, issue);
|
archivedIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,15 +33,14 @@ export const ArchivedIssueListLayout: FC = observer(() => {
|
|||||||
return projectStore?.projects[workspaceSlug.toString()] || null;
|
return projectStore?.projects[workspaceSlug.toString()] || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
<BaseListRoot
|
||||||
// return (
|
issueFilterStore={archivedIssueFiltersStore}
|
||||||
// <BaseListRoot
|
issueStore={archivedIssueStore}
|
||||||
// issueFilterStore={archivedIssueFiltersStore}
|
QuickActions={ArchivedIssueQuickActions}
|
||||||
// issueStore={archivedIssueStore}
|
issueActions={issueActions}
|
||||||
// QuickActions={ArchivedIssueQuickActions}
|
getProjects={getProjects}
|
||||||
// issueActions={issueActions}
|
currentStore={EProjectStore.PROJECT}
|
||||||
// getProjects={getProjects}
|
/>
|
||||||
// />
|
);
|
||||||
// );
|
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ import { IIssue } from "types";
|
|||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
import { IProjectStore } from "store/project";
|
import { IProjectStore } from "store/project";
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface ICycleListLayout {}
|
export interface ICycleListLayout {}
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ export const CycleListLayout: React.FC = observer(() => {
|
|||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
getProjects={getProjects}
|
getProjects={getProjects}
|
||||||
viewId={cycleId}
|
viewId={cycleId}
|
||||||
|
currentStore={EProjectStore.CYCLE}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// hooks
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// components
|
||||||
|
import { ProjectIssueQuickActions } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IIssue } from "types";
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
// constants
|
||||||
|
import { BaseListRoot } from "../base-list-root";
|
||||||
|
import { IProjectStore } from "store/project";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
|
export const DraftIssueListLayout: FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
|
if (!workspaceSlug || !projectId) return null;
|
||||||
|
|
||||||
|
// store
|
||||||
|
const { projectDraftIssuesFilter: projectIssuesFilterStore, projectDraftIssues: projectIssuesStore } = useMobxStore();
|
||||||
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseListRoot
|
||||||
|
issueFilterStore={projectIssuesFilterStore}
|
||||||
|
issueStore={projectIssuesStore}
|
||||||
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
issueActions={issueActions}
|
||||||
|
getProjects={getProjects}
|
||||||
|
currentStore={EProjectStore.PROJECT}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
@ -11,6 +11,7 @@ import { EIssueActions } from "../../types";
|
|||||||
// constants
|
// constants
|
||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
import { IProjectStore } from "store/project";
|
import { IProjectStore } from "store/project";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IModuleListLayout {}
|
export interface IModuleListLayout {}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ export const ModuleListLayout: React.FC = observer(() => {
|
|||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
getProjects={getProjects}
|
getProjects={getProjects}
|
||||||
viewId={moduleId}
|
viewId={moduleId}
|
||||||
|
currentStore={EProjectStore.MODULE}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,50 +1,49 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// mobx store
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { ProjectIssueQuickActions } from "components/issues";
|
import { ProjectIssueQuickActions } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { EIssueActions } from "../../types";
|
import { EIssueActions } from "../../types";
|
||||||
import { IProjectStore } from "store/project";
|
// constants
|
||||||
//components
|
|
||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
|
import { IProjectStore } from "store/project";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ProfileIssuesListLayout: FC = observer(() => {
|
export const ProfileIssuesListLayout: FC = observer(() => {
|
||||||
const {
|
|
||||||
profileIssueFilters: profileIssueFiltersStore,
|
|
||||||
profileIssues: profileIssuesStore,
|
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
} = useMobxStore();
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug, userId } = router.query as { workspaceSlug: string; userId: string };
|
||||||
|
|
||||||
|
// store
|
||||||
|
const { workspaceProfileIssuesFilter: profileIssueFiltersStore, workspaceProfileIssues: profileIssuesStore } =
|
||||||
|
useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug || !userId) return;
|
||||||
|
|
||||||
profileIssuesStore.updateIssueStructure(group_by, null, issue);
|
await profileIssuesStore.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
profileIssuesStore.deleteIssue(group_by, null, issue);
|
if (!workspaceSlug || !userId) return;
|
||||||
|
|
||||||
|
await profileIssuesStore.removeIssue(workspaceSlug, userId, issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProjects = (projectStore: IProjectStore) => projectStore?.workspaceProjects || null;
|
const getProjects = (projectStore: IProjectStore) => projectStore.workspaceProjects;
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
<BaseListRoot
|
||||||
// return (
|
issueFilterStore={profileIssueFiltersStore}
|
||||||
// <BaseListRoot
|
issueStore={profileIssuesStore}
|
||||||
// issueFilterStore={profileIssueFiltersStore}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
// issueStore={profileIssuesStore}
|
issueActions={issueActions}
|
||||||
// QuickActions={ProjectIssueQuickActions}
|
getProjects={getProjects}
|
||||||
// issueActions={issueActions}
|
currentStore={EProjectStore.PROFILE}
|
||||||
// getProjects={getProjects}
|
/>
|
||||||
// />
|
);
|
||||||
// );
|
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ import { EIssueActions } from "../../types";
|
|||||||
// constants
|
// constants
|
||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
import { IProjectStore } from "store/project";
|
import { IProjectStore } from "store/project";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ListLayout: FC = observer(() => {
|
export const ListLayout: FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -41,6 +42,7 @@ export const ListLayout: FC = observer(() => {
|
|||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
getProjects={getProjects}
|
getProjects={getProjects}
|
||||||
|
currentStore={EProjectStore.PROJECT}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,7 @@ import { IIssue } from "types";
|
|||||||
// components
|
// components
|
||||||
import { BaseListRoot } from "../base-list-root";
|
import { BaseListRoot } from "../base-list-root";
|
||||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IViewListLayout {}
|
export interface IViewListLayout {}
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ export const ProjectViewListLayout: React.FC = observer(() => {
|
|||||||
QuickActions={ProjectIssueQuickActions}
|
QuickActions={ProjectIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
getProjects={getProjects}
|
getProjects={getProjects}
|
||||||
|
currentStore={EProjectStore.PROJECT_VIEW}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -43,7 +43,9 @@ export const IssuePropertyDate: React.FC<IIssuePropertyDate> = observer((props)
|
|||||||
<Popover.Button
|
<Popover.Button
|
||||||
ref={dropdownBtn}
|
ref={dropdownBtn}
|
||||||
className={`px-2.5 py-1 h-5 flex items-center rounded border-[0.5px] border-custom-border-300 duration-300 outline-none w-full ${
|
className={`px-2.5 py-1 h-5 flex items-center rounded border-[0.5px] border-custom-border-300 duration-300 outline-none w-full ${
|
||||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
disabled
|
||||||
|
? "cursor-not-allowed text-custom-text-200 pointer-events-none"
|
||||||
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="overflow-hidden flex justify-center items-center gap-2">
|
<div className="overflow-hidden flex justify-center items-center gap-2">
|
||||||
|
@ -59,6 +59,8 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
if (workspaceSlug && projectId) fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
if (workspaceSlug && projectId) fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!value) return null;
|
||||||
|
|
||||||
const options = (projectLabels ? projectLabels : []).map((label) => ({
|
const options = (projectLabels ? projectLabels : []).map((label) => ({
|
||||||
value: label.id,
|
value: label.id,
|
||||||
query: label.name,
|
query: label.name,
|
||||||
|
@ -11,6 +11,7 @@ import { copyUrlToClipboard } from "helpers/string.helper";
|
|||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const CycleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
export const CycleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
||||||
const { issue, handleDelete, handleUpdate, handleRemoveFromView } = props;
|
const { issue, handleDelete, handleUpdate, handleRemoveFromView } = props;
|
||||||
@ -55,6 +56,7 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
|||||||
onSubmit={async (data) => {
|
onSubmit={async (data) => {
|
||||||
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
||||||
}}
|
}}
|
||||||
|
currentStore={EProjectStore.CYCLE}
|
||||||
/>
|
/>
|
||||||
<CustomMenu placement="bottom-start" ellipsis>
|
<CustomMenu placement="bottom-start" ellipsis>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
@ -11,6 +11,7 @@ import { copyUrlToClipboard } from "helpers/string.helper";
|
|||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
||||||
const { issue, handleDelete, handleUpdate, handleRemoveFromView } = props;
|
const { issue, handleDelete, handleUpdate, handleRemoveFromView } = props;
|
||||||
@ -55,6 +56,7 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
|||||||
onSubmit={async (data) => {
|
onSubmit={async (data) => {
|
||||||
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
||||||
}}
|
}}
|
||||||
|
currentStore={EProjectStore.MODULE}
|
||||||
/>
|
/>
|
||||||
<CustomMenu placement="bottom-start" ellipsis>
|
<CustomMenu placement="bottom-start" ellipsis>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
@ -11,6 +11,7 @@ import { copyUrlToClipboard } from "helpers/string.helper";
|
|||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
|
||||||
const { issue, handleDelete, handleUpdate } = props;
|
const { issue, handleDelete, handleUpdate } = props;
|
||||||
@ -55,6 +56,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = (props) =>
|
|||||||
onSubmit={async (data) => {
|
onSubmit={async (data) => {
|
||||||
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
if (issueToEdit && handleUpdate) handleUpdate({ ...issueToEdit, ...data });
|
||||||
}}
|
}}
|
||||||
|
currentStore={EProjectStore.PROJECT}
|
||||||
/>
|
/>
|
||||||
<CustomMenu placement="bottom-start" ellipsis>
|
<CustomMenu placement="bottom-start" ellipsis>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
@ -9,14 +9,17 @@ import { ArchivedIssueListLayout, ArchivedIssueAppliedFiltersRoot } from "compon
|
|||||||
|
|
||||||
export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const { archivedIssueFilters: archivedIssueFiltersStore, archivedIssues: archivedIssueStore } = useMobxStore();
|
const {
|
||||||
|
projectArchivedIssues: { getIssues, fetchIssues },
|
||||||
|
projectArchivedIssuesFilter: { fetchFilters },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId ? `ARCHIVED_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
useSWR(workspaceSlug && projectId ? `ARCHIVED_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
||||||
if (workspaceSlug && projectId) {
|
if (workspaceSlug && projectId) {
|
||||||
await archivedIssueFiltersStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
await fetchFilters(workspaceSlug, projectId);
|
||||||
await archivedIssueStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import useSWR from "swr";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { DraftIssueAppliedFiltersRoot } from "../filters/applied-filters/roots/draft-issue";
|
||||||
|
import { DraftIssueListLayout } from "../list/roots/draft-issue-root";
|
||||||
|
import { Spinner } from "@plane/ui";
|
||||||
|
import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root";
|
||||||
|
|
||||||
|
export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
|
const {
|
||||||
|
projectDraftIssuesFilter: { issueFilters, fetchFilters },
|
||||||
|
projectDraftIssues: { loader, getIssues, fetchIssues },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
|
useSWR(workspaceSlug && projectId ? `DRAFT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
||||||
|
if (workspaceSlug && projectId) {
|
||||||
|
await fetchFilters(workspaceSlug, projectId);
|
||||||
|
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeLayout = issueFilters?.displayFilters?.layout || undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-full h-full flex flex-col overflow-hidden">
|
||||||
|
<DraftIssueAppliedFiltersRoot />
|
||||||
|
|
||||||
|
{loader === "init-loader" ? (
|
||||||
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="w-full h-full relative overflow-auto">
|
||||||
|
{activeLayout === "list" ? (
|
||||||
|
<DraftIssueListLayout />
|
||||||
|
) : activeLayout === "kanban" ? (
|
||||||
|
<DraftKanBanLayout />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -102,17 +102,15 @@ export const GlobalViewLayoutRoot: React.FC<Props> = observer((props) => {
|
|||||||
<GlobalViewEmptyState />
|
<GlobalViewEmptyState />
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full w-full overflow-auto">
|
<div className="h-full w-full overflow-auto">
|
||||||
<SpreadsheetView
|
{/* <SpreadsheetView
|
||||||
displayProperties={workspaceFilterStore.workspaceDisplayProperties}
|
displayProperties={workspaceFilterStore.workspaceDisplayProperties}
|
||||||
displayFilters={workspaceFilterStore.workspaceDisplayFilters}
|
displayFilters={workspaceFilterStore.workspaceDisplayFilters}
|
||||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||||
issues={issues}
|
issues={issues}
|
||||||
members={workspaceMembers?.map((m) => m.member)}
|
members={workspaceMembers?.map((m) => m.member)}
|
||||||
labels={workspaceStore.workspaceLabels ? workspaceStore.workspaceLabels : undefined}
|
labels={workspaceStore.workspaceLabels ? workspaceStore.workspaceLabels : undefined}
|
||||||
handleIssueAction={() => {}}
|
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
|
||||||
disableUserActions={false}
|
disableUserActions={false}
|
||||||
/>
|
/> */}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,12 +26,20 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
|
|||||||
projectIssuesFilter: { issueFilters, fetchFilters },
|
projectIssuesFilter: { issueFilters, fetchFilters },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => {
|
useSWR(
|
||||||
if (workspaceSlug && projectId) {
|
workspaceSlug && projectId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null,
|
||||||
await fetchFilters(workspaceSlug, projectId);
|
async () => {
|
||||||
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
if (workspaceSlug && projectId) {
|
||||||
|
await fetchFilters(workspaceSlug, projectId);
|
||||||
|
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onErrorRetry: (error) => {
|
||||||
|
if (error.status === 404) return;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
|
@ -7,12 +7,11 @@ import useSWR from "swr";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
ModuleKanBanLayout,
|
|
||||||
ModuleListLayout,
|
|
||||||
ProjectViewAppliedFiltersRoot,
|
ProjectViewAppliedFiltersRoot,
|
||||||
ProjectViewCalendarLayout,
|
ProjectViewCalendarLayout,
|
||||||
ProjectViewEmptyState,
|
|
||||||
ProjectViewGanttLayout,
|
ProjectViewGanttLayout,
|
||||||
|
ProjectViewKanBanLayout,
|
||||||
|
ProjectViewListLayout,
|
||||||
ProjectViewSpreadsheetLayout,
|
ProjectViewSpreadsheetLayout,
|
||||||
} from "components/issues";
|
} from "components/issues";
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
@ -33,7 +32,7 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
useSWR(workspaceSlug && projectId && viewId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => {
|
useSWR(workspaceSlug && projectId && viewId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => {
|
||||||
if (workspaceSlug && projectId && viewId) {
|
if (workspaceSlug && projectId && viewId) {
|
||||||
await fetchFilters(workspaceSlug, projectId, viewId);
|
await fetchFilters(workspaceSlug, projectId, viewId);
|
||||||
// await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,12 +48,11 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* {(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 && <ProjectViewEmptyState />} */}
|
|
||||||
<div className="w-full h-full relative overflow-auto">
|
<div className="w-full h-full relative overflow-auto">
|
||||||
{activeLayout === "list" ? (
|
{activeLayout === "list" ? (
|
||||||
<ModuleListLayout />
|
<ProjectViewListLayout />
|
||||||
) : activeLayout === "kanban" ? (
|
) : activeLayout === "kanban" ? (
|
||||||
<ModuleKanBanLayout />
|
<ProjectViewKanBanLayout />
|
||||||
) : activeLayout === "calendar" ? (
|
) : activeLayout === "calendar" ? (
|
||||||
<ProjectViewCalendarLayout />
|
<ProjectViewCalendarLayout />
|
||||||
) : activeLayout === "gantt_chart" ? (
|
) : activeLayout === "gantt_chart" ? (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { IIssueUnGroupedStructure } from "store/issue";
|
import { IIssueUnGroupedStructure } from "store/issue";
|
||||||
import { SpreadsheetView } from "./spreadsheet-view";
|
import { SpreadsheetView } from "./spreadsheet-view";
|
||||||
import { useCallback } from "react";
|
import { FC, useCallback } from "react";
|
||||||
import { IIssue, IIssueDisplayFilterOptions } from "types";
|
import { IIssue, IIssueDisplayFilterOptions } from "types";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
@ -16,6 +16,8 @@ import {
|
|||||||
} from "store/issues";
|
} from "store/issues";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { EFilterType, TUnGroupedIssues } from "store/issues/types";
|
import { EFilterType, TUnGroupedIssues } from "store/issues/types";
|
||||||
|
import { EIssueActions } from "../types";
|
||||||
|
import { IQuickActionProps } from "../list/list-view-types";
|
||||||
|
|
||||||
interface IBaseSpreadsheetRoot {
|
interface IBaseSpreadsheetRoot {
|
||||||
issueFiltersStore:
|
issueFiltersStore:
|
||||||
@ -25,16 +27,21 @@ interface IBaseSpreadsheetRoot {
|
|||||||
| IProjectIssuesFilterStore;
|
| IProjectIssuesFilterStore;
|
||||||
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
issueStore: IProjectIssuesStore | IModuleIssuesStore | ICycleIssuesStore | IViewIssuesStore;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
|
QuickActions: FC<IQuickActionProps>;
|
||||||
|
issueActions: {
|
||||||
|
[EIssueActions.DELETE]: (issue: IIssue) => void;
|
||||||
|
[EIssueActions.UPDATE]?: (issue: IIssue) => void;
|
||||||
|
[EIssueActions.REMOVE]?: (issue: IIssue) => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||||
const { issueFiltersStore, issueStore, viewId } = props;
|
const { issueFiltersStore, issueStore, viewId, QuickActions, issueActions } = props;
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||||
|
|
||||||
const {
|
const {
|
||||||
issueDetail: issueDetailStore,
|
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
projectState: projectStateStore,
|
projectState: projectStateStore,
|
||||||
projectLabel: { projectLabels },
|
projectLabel: { projectLabels },
|
||||||
@ -46,19 +53,16 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
|||||||
const issuesResponse = issueStore.getIssues;
|
const issuesResponse = issueStore.getIssues;
|
||||||
const issueIds = (issueStore.getIssuesIds ?? []) as TUnGroupedIssues;
|
const issueIds = (issueStore.getIssuesIds ?? []) as TUnGroupedIssues;
|
||||||
|
|
||||||
const issues = issueIds?.map((id) => issuesResponse?.[id]);
|
const issues = issueIds?.filter((id) => id && issuesResponse?.[id]).map((id) => issuesResponse?.[id]);
|
||||||
|
|
||||||
const handleIssueAction = async (issue: IIssue, action: "copy" | "delete" | "edit") => {
|
const handleIssues = useCallback(
|
||||||
if (!workspaceSlug || !projectId || !user) return;
|
async (issue: IIssue, action: EIssueActions) => {
|
||||||
|
if (issueActions[action]) {
|
||||||
if (action === "delete") {
|
issueActions[action]!(issue);
|
||||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), projectId.toString(), issue.id);
|
}
|
||||||
// issueStore.removeIssueFromStructure(null, null, issue);
|
},
|
||||||
} else if (action === "edit") {
|
[issueStore]
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, issue);
|
);
|
||||||
// issueStore.updateIssueStructure(null, null, issue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDisplayFiltersUpdate = useCallback(
|
const handleDisplayFiltersUpdate = useCallback(
|
||||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||||
@ -77,33 +81,28 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
|||||||
[issueFiltersStore, projectId, workspaceSlug]
|
[issueFiltersStore, projectId, workspaceSlug]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleUpdateIssue = useCallback(
|
|
||||||
(issue: IIssue, data: Partial<IIssue>) => {
|
|
||||||
if (!workspaceSlug || !projectId || !user) return;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
...issue,
|
|
||||||
...data,
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: add update logic from the new store
|
|
||||||
// issueStore.updateIssueStructure(null, null, payload);
|
|
||||||
issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data);
|
|
||||||
},
|
|
||||||
[issueDetailStore, projectId, user, workspaceSlug]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SpreadsheetView
|
<SpreadsheetView
|
||||||
displayProperties={issueFiltersStore.issueFilters?.displayProperties ?? {}}
|
displayProperties={issueFiltersStore.issueFilters?.displayProperties ?? {}}
|
||||||
displayFilters={issueFiltersStore.issueFilters?.displayFilters ?? {}}
|
displayFilters={issueFiltersStore.issueFilters?.displayFilters ?? {}}
|
||||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||||
issues={issues as IIssueUnGroupedStructure}
|
issues={issues as IIssueUnGroupedStructure}
|
||||||
|
quickActions={(issue) => (
|
||||||
|
<QuickActions
|
||||||
|
issue={issue}
|
||||||
|
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||||
|
handleUpdate={
|
||||||
|
issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined
|
||||||
|
}
|
||||||
|
handleRemoveFromView={
|
||||||
|
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
members={projectMembers?.map((m) => m.member)}
|
members={projectMembers?.map((m) => m.member)}
|
||||||
labels={projectLabels || undefined}
|
labels={projectLabels || undefined}
|
||||||
states={projectId ? projectStateStore.states?.[projectId.toString()] : undefined}
|
states={projectId ? projectStateStore.states?.[projectId.toString()] : undefined}
|
||||||
handleIssueAction={handleIssueAction}
|
handleIssues={handleIssues}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
|
||||||
disableUserActions={false}
|
disableUserActions={false}
|
||||||
quickAddCallback={issueStore.quickAddIssue}
|
quickAddCallback={issueStore.quickAddIssue}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
|
@ -18,12 +18,12 @@ type Props = {
|
|||||||
export const SpreadsheetAssigneeColumn: React.FC<Props> = ({ issue, members, onChange, expandedIssues, disabled }) => {
|
export const SpreadsheetAssigneeColumn: React.FC<Props> = ({ issue, members, onChange, expandedIssues, disabled }) => {
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssuePropertyAssignee
|
<IssuePropertyAssignee
|
||||||
projectId={issue.project_detail.id ?? null}
|
projectId={issue.project_detail?.id ?? null}
|
||||||
value={issue.assignees}
|
value={issue.assignees}
|
||||||
onChange={(data) => onChange({ assignees: data })}
|
onChange={(data) => onChange({ assignees: data })}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
|
@ -14,7 +14,7 @@ export const SpreadsheetAttachmentColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -17,7 +17,7 @@ type Props = {
|
|||||||
export const SpreadsheetDueDateColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
export const SpreadsheetDueDateColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -17,12 +17,12 @@ export const SpreadsheetEstimateColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssuePropertyEstimates
|
<IssuePropertyEstimates
|
||||||
projectId={issue.project_detail.id ?? null}
|
projectId={issue.project_detail?.id ?? null}
|
||||||
value={issue.estimate_point}
|
value={issue.estimate_point}
|
||||||
onChange={(data) => onChange({ estimate_point: data })}
|
onChange={(data) => onChange({ estimate_point: data })}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { Popover2 } from "@blueprintjs/popover2";
|
import { ChevronRight } from "lucide-react";
|
||||||
import { MoreHorizontal, Pencil, Trash2, ChevronRight, Link } from "lucide-react";
|
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
@ -16,8 +15,7 @@ type Props = {
|
|||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
handleToggleExpand: (issueId: string) => void;
|
handleToggleExpand: (issueId: string) => void;
|
||||||
properties: IIssueDisplayProperties;
|
properties: IIssueDisplayProperties;
|
||||||
handleEditIssue: (issue: IIssue) => void;
|
quickActions: (issue: IIssue) => React.ReactNode;
|
||||||
handleDeleteIssue: (issue: IIssue) => void;
|
|
||||||
setIssuePeekOverView: React.Dispatch<
|
setIssuePeekOverView: React.Dispatch<
|
||||||
React.SetStateAction<{
|
React.SetStateAction<{
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -35,8 +33,7 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
handleToggleExpand,
|
handleToggleExpand,
|
||||||
setIssuePeekOverView,
|
setIssuePeekOverView,
|
||||||
properties,
|
properties,
|
||||||
handleEditIssue,
|
quickActions,
|
||||||
handleDeleteIssue,
|
|
||||||
disableUserActions,
|
disableUserActions,
|
||||||
nestingLevel,
|
nestingLevel,
|
||||||
}) => {
|
}) => {
|
||||||
@ -75,7 +72,7 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="group flex items-center w-[28rem] text-sm h-11 sticky top-0 bg-custom-background-100 truncate border-b border-custom-border-100">
|
<div className="group flex items-center w-[28rem] text-sm h-11 top-0 bg-custom-background-100 truncate border-b border-custom-border-100">
|
||||||
{properties.key && (
|
{properties.key && (
|
||||||
<div
|
<div
|
||||||
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center min-w-min"
|
className="flex gap-1.5 px-4 pr-0 py-2.5 items-center min-w-min"
|
||||||
@ -87,61 +84,7 @@ export const IssueColumn: React.FC<Props> = ({
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{!disableUserActions && (
|
{!disableUserActions && (
|
||||||
<div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">
|
<div className="absolute top-0 left-2.5 opacity-0 group-hover:opacity-100">{quickActions(issue)}</div>
|
||||||
<Popover2
|
|
||||||
isOpen={isOpen}
|
|
||||||
canEscapeKeyClose
|
|
||||||
onInteraction={(nextOpenState) => setIsOpen(nextOpenState)}
|
|
||||||
content={
|
|
||||||
<div className="flex flex-col whitespace-nowrap rounded-md border border-custom-border-100 p-1 text-xs shadow-lg focus:outline-none min-w-full bg-custom-background-100 space-y-0.5">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
|
||||||
onClick={() => {
|
|
||||||
handleCopyText();
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Link className="h-3 w-3" />
|
|
||||||
<span>Copy link</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="hover:text-custom-text-200 w-full select-none gap-2 rounded p-1 text-left text-custom-text-200 hover:bg-custom-background-80"
|
|
||||||
onClick={() => {
|
|
||||||
handleEditIssue(issue);
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Pencil className="h-3 w-3" />
|
|
||||||
<span>Edit issue</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="w-full select-none gap-2 rounded p-1 text-left text-red-500 hover:bg-custom-background-80"
|
|
||||||
onClick={() => {
|
|
||||||
handleDeleteIssue(issue);
|
|
||||||
setIsOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Trash2 className="h-3 w-3" />
|
|
||||||
<span>Delete issue</span>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
placement="bottom-start"
|
|
||||||
>
|
|
||||||
<MoreHorizontal className="h-5 w-5 text-custom-text-200" />
|
|
||||||
</Popover2>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -6,13 +6,14 @@ import { IssueColumn } from "components/issues";
|
|||||||
import useSubIssue from "hooks/use-sub-issue";
|
import useSubIssue from "hooks/use-sub-issue";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayProperties } from "types";
|
import { IIssue, IIssueDisplayProperties } from "types";
|
||||||
|
import { EIssueActions } from "components/issues/issue-layouts/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
expandedIssues: string[];
|
expandedIssues: string[];
|
||||||
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
properties: IIssueDisplayProperties;
|
properties: IIssueDisplayProperties;
|
||||||
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
quickActions: (issue: IIssue) => React.ReactNode;
|
||||||
setIssuePeekOverView: React.Dispatch<
|
setIssuePeekOverView: React.Dispatch<
|
||||||
React.SetStateAction<{
|
React.SetStateAction<{
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -30,7 +31,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
setExpandedIssues,
|
setExpandedIssues,
|
||||||
setIssuePeekOverView,
|
setIssuePeekOverView,
|
||||||
properties,
|
properties,
|
||||||
handleIssueAction,
|
quickActions,
|
||||||
disableUserActions,
|
disableUserActions,
|
||||||
nestingLevel = 0,
|
nestingLevel = 0,
|
||||||
}) => {
|
}) => {
|
||||||
@ -48,7 +49,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -57,11 +58,10 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
expanded={isExpanded}
|
expanded={isExpanded}
|
||||||
handleToggleExpand={handleToggleExpand}
|
handleToggleExpand={handleToggleExpand}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
handleEditIssue={() => handleIssueAction(issue, "edit")}
|
|
||||||
handleDeleteIssue={() => handleIssueAction(issue, "delete")}
|
|
||||||
setIssuePeekOverView={setIssuePeekOverView}
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
nestingLevel={nestingLevel}
|
nestingLevel={nestingLevel}
|
||||||
|
quickActions={quickActions}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
@ -75,7 +75,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
|||||||
expandedIssues={expandedIssues}
|
expandedIssues={expandedIssues}
|
||||||
setExpandedIssues={setExpandedIssues}
|
setExpandedIssues={setExpandedIssues}
|
||||||
properties={properties}
|
properties={properties}
|
||||||
handleIssueAction={handleIssueAction}
|
quickActions={quickActions}
|
||||||
setIssuePeekOverView={setIssuePeekOverView}
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
nestingLevel={nestingLevel + 1}
|
nestingLevel={nestingLevel + 1}
|
||||||
|
@ -20,12 +20,12 @@ export const SpreadsheetLabelColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssuePropertyLabels
|
<IssuePropertyLabels
|
||||||
projectId={issue.project_detail.id ?? null}
|
projectId={issue.project_detail?.id ?? null}
|
||||||
value={issue.labels}
|
value={issue.labels}
|
||||||
onChange={(data) => onChange({ labels: data })}
|
onChange={(data) => onChange({ labels: data })}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
|
@ -14,7 +14,7 @@ export const SpreadsheetLinkColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -17,7 +17,7 @@ type Props = {
|
|||||||
export const SpreadsheetPriorityColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
export const SpreadsheetPriorityColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -17,7 +17,7 @@ type Props = {
|
|||||||
export const SpreadsheetStartDateColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
export const SpreadsheetStartDateColumn: React.FC<Props> = ({ issue, onChange, expandedIssues, disabled }) => {
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -20,12 +20,12 @@ export const SpreadsheetStateColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssuePropertyState
|
<IssuePropertyState
|
||||||
projectId={issue.project_detail.id ?? null}
|
projectId={issue.project_detail?.id ?? null}
|
||||||
value={issue.state_detail}
|
value={issue.state_detail}
|
||||||
onChange={(data) => onChange({ state: data.id, state_detail: data })}
|
onChange={(data) => onChange({ state: data.id, state_detail: data })}
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
|
@ -14,7 +14,7 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -17,7 +17,7 @@ export const SpreadsheetUpdatedOnColumn: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
const isExpanded = expandedIssues.indexOf(issue.id) > -1;
|
||||||
|
|
||||||
const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded);
|
const { subIssues, isLoading } = useSubIssue(issue.project_detail?.id, issue.id, isExpanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -5,14 +5,39 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
import { IIssue } from "types";
|
||||||
|
import { CycleIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
|
|
||||||
export const CycleSpreadsheetLayout: React.FC = observer(() => {
|
export const CycleSpreadsheetLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { cycleId } = router.query as { cycleId: string };
|
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||||
|
|
||||||
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
|
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
|
||||||
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !cycleId) return;
|
||||||
|
|
||||||
|
cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !cycleId) return;
|
||||||
|
cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
||||||
|
},
|
||||||
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
||||||
|
cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseSpreadsheetRoot issueStore={cycleIssueStore} issueFiltersStore={cycleIssueFilterStore} viewId={cycleId} />
|
<BaseSpreadsheetRoot
|
||||||
|
issueStore={cycleIssueStore}
|
||||||
|
issueFiltersStore={cycleIssueFilterStore}
|
||||||
|
viewId={cycleId}
|
||||||
|
issueActions={issueActions}
|
||||||
|
QuickActions={CycleIssueQuickActions}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -6,13 +6,39 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// components
|
// components
|
||||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
import { IIssue } from "types";
|
||||||
|
import { ModuleIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
|
|
||||||
export const ModuleSpreadsheetLayout: React.FC = observer(() => {
|
export const ModuleSpreadsheetLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { moduleId } = router.query as { moduleId: string };
|
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||||
|
|
||||||
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
|
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
|
||||||
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !moduleId) return;
|
||||||
|
|
||||||
|
moduleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, moduleId);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !moduleId) return;
|
||||||
|
moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
||||||
|
},
|
||||||
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||||
|
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseSpreadsheetRoot issueStore={moduleIssueStore} issueFiltersStore={moduleIssueFilterStore} viewId={moduleId} />
|
<BaseSpreadsheetRoot
|
||||||
|
issueStore={moduleIssueStore}
|
||||||
|
issueFiltersStore={moduleIssueFilterStore}
|
||||||
|
viewId={moduleId}
|
||||||
|
issueActions={issueActions}
|
||||||
|
QuickActions={ModuleIssueQuickActions}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,36 @@ import { observer } from "mobx-react-lite";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
import { IIssue } from "types";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
|
|
||||||
export const ProjectSpreadsheetLayout: React.FC = observer(() => {
|
export const ProjectSpreadsheetLayout: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||||
|
|
||||||
const { projectIssues: projectIssuesStore, projectIssuesFilter: projectIssueFiltersStore } = useMobxStore();
|
const { projectIssues: projectIssuesStore, projectIssuesFilter: projectIssueFiltersStore } = useMobxStore();
|
||||||
return <BaseSpreadsheetRoot issueStore={projectIssuesStore} issueFiltersStore={projectIssueFiltersStore} />;
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await projectIssuesStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await projectIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseSpreadsheetRoot
|
||||||
|
issueStore={projectIssuesStore}
|
||||||
|
issueFiltersStore={projectIssueFiltersStore}
|
||||||
|
issueActions={issueActions}
|
||||||
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,36 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||||
|
import { EIssueActions } from "../../types";
|
||||||
|
import { IIssue } from "types";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||||
|
|
||||||
export const ProjectViewSpreadsheetLayout: React.FC = observer(() => {
|
export const ProjectViewSpreadsheetLayout: React.FC = observer(() => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||||
|
|
||||||
const { viewIssues: projectViewIssuesStore, viewIssuesFilter: projectViewIssueFiltersStore } = useMobxStore();
|
const { viewIssues: projectViewIssuesStore, viewIssuesFilter: projectViewIssueFiltersStore } = useMobxStore();
|
||||||
return <BaseSpreadsheetRoot issueStore={projectViewIssuesStore} issueFiltersStore={projectViewIssueFiltersStore} />;
|
|
||||||
|
const issueActions = {
|
||||||
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await projectViewIssuesStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
||||||
|
},
|
||||||
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
|
await projectViewIssuesStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseSpreadsheetRoot
|
||||||
|
issueStore={projectViewIssuesStore}
|
||||||
|
issueFiltersStore={projectViewIssueFiltersStore}
|
||||||
|
issueActions={issueActions}
|
||||||
|
QuickActions={ProjectIssueQuickActions}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
|||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueLabel, IState, IUserLite } from "types";
|
import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueLabel, IState, IUserLite } from "types";
|
||||||
|
import { EIssueActions } from "../types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
displayProperties: IIssueDisplayProperties;
|
displayProperties: IIssueDisplayProperties;
|
||||||
@ -16,8 +17,8 @@ type Props = {
|
|||||||
members?: IUserLite[] | undefined;
|
members?: IUserLite[] | undefined;
|
||||||
labels?: IIssueLabel[] | undefined;
|
labels?: IIssueLabel[] | undefined;
|
||||||
states?: IState[] | undefined;
|
states?: IState[] | undefined;
|
||||||
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
quickActions: (issue: IIssue) => React.ReactNode;
|
||||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
||||||
openIssuesListModal?: (() => void) | null;
|
openIssuesListModal?: (() => void) | null;
|
||||||
quickAddCallback?: (
|
quickAddCallback?: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
@ -39,8 +40,8 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
members,
|
members,
|
||||||
labels,
|
labels,
|
||||||
states,
|
states,
|
||||||
handleIssueAction,
|
quickActions,
|
||||||
handleUpdateIssue,
|
handleIssues,
|
||||||
quickAddCallback,
|
quickAddCallback,
|
||||||
viewId,
|
viewId,
|
||||||
disableUserActions,
|
disableUserActions,
|
||||||
@ -80,6 +81,8 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
console.log("spreadsheet issues", issues);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-full w-full rounded-lg text-custom-text-200 overflow-x-auto whitespace-nowrap bg-custom-background-200">
|
<div className="relative flex h-full w-full rounded-lg text-custom-text-200 overflow-x-auto whitespace-nowrap bg-custom-background-200">
|
||||||
<div className="h-full w-full flex flex-col">
|
<div className="h-full w-full flex flex-col">
|
||||||
@ -103,18 +106,20 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
<span className="flex items-center justify-center px-4 py-2.5 h-full w-full flex-grow">Issue</span>
|
<span className="flex items-center justify-center px-4 py-2.5 h-full w-full flex-grow">Issue</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{issues.map((issue, index) => (
|
{issues.map((issue, index) =>
|
||||||
<SpreadsheetIssuesColumn
|
issue ? (
|
||||||
key={`${issue.id}_${index}`}
|
<SpreadsheetIssuesColumn
|
||||||
issue={issue}
|
key={`${issue?.id}_${index}`}
|
||||||
expandedIssues={expandedIssues}
|
issue={issue}
|
||||||
setExpandedIssues={setExpandedIssues}
|
expandedIssues={expandedIssues}
|
||||||
properties={displayProperties}
|
setExpandedIssues={setExpandedIssues}
|
||||||
handleIssueAction={handleIssueAction}
|
properties={displayProperties}
|
||||||
disableUserActions={disableUserActions}
|
quickActions={quickActions}
|
||||||
setIssuePeekOverView={setIssuePeekOverView}
|
disableUserActions={disableUserActions}
|
||||||
/>
|
setIssuePeekOverView={setIssuePeekOverView}
|
||||||
))}
|
/>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -124,7 +129,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
expandedIssues={expandedIssues}
|
expandedIssues={expandedIssues}
|
||||||
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
handleUpdateIssue={(issue, data) => handleIssues({ ...issue, ...data }, EIssueActions.UPDATE)}
|
||||||
issues={issues}
|
issues={issues}
|
||||||
members={members}
|
members={members}
|
||||||
labels={labels}
|
labels={labels}
|
||||||
@ -185,7 +190,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
workspaceSlug={issuePeekOverview?.workspaceSlug}
|
workspaceSlug={issuePeekOverview?.workspaceSlug}
|
||||||
projectId={issuePeekOverview?.projectId}
|
projectId={issuePeekOverview?.projectId}
|
||||||
issueId={issuePeekOverview?.issueId}
|
issueId={issuePeekOverview?.issueId}
|
||||||
handleIssue={(issueToUpdate: any) => handleUpdateIssue(issueToUpdate as IIssue, issueToUpdate)}
|
handleIssue={(issueToUpdate: any) => handleIssues(issueToUpdate, EIssueActions.UPDATE)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,7 @@ import { IssueForm, ConfirmIssueDiscard } from "components/issues";
|
|||||||
import type { IIssue } from "types";
|
import type { IIssue } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { USER_ISSUE, SUB_ISSUES } from "constants/fetch-keys";
|
import { USER_ISSUE, SUB_ISSUES } from "constants/fetch-keys";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export interface IssuesModalProps {
|
export interface IssuesModalProps {
|
||||||
data?: IIssue | null;
|
data?: IIssue | null;
|
||||||
@ -40,6 +41,7 @@ export interface IssuesModalProps {
|
|||||||
)[];
|
)[];
|
||||||
onSubmit?: (data: Partial<IIssue>) => Promise<void>;
|
onSubmit?: (data: Partial<IIssue>) => Promise<void>;
|
||||||
handleSubmit?: (data: Partial<IIssue>) => Promise<void>;
|
handleSubmit?: (data: Partial<IIssue>) => Promise<void>;
|
||||||
|
currentStore?: EProjectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
const issueDraftService = new IssueDraftService();
|
const issueDraftService = new IssueDraftService();
|
||||||
@ -53,6 +55,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
fieldsToShow = ["all"],
|
fieldsToShow = ["all"],
|
||||||
onSubmit,
|
onSubmit,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
|
currentStore = EProjectStore.PROJECT,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
@ -63,20 +66,56 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
const [prePopulateData, setPreloadedData] = useState<Partial<IIssue>>({});
|
const [prePopulateData, setPreloadedData] = useState<Partial<IIssue>>({});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
const { workspaceSlug, projectId, cycleId, moduleId } = router.query as {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string | undefined;
|
||||||
|
cycleId: string | undefined;
|
||||||
|
moduleId: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
issue: issueStore,
|
projectIssues: projectIssueStore,
|
||||||
issueDetail: issueDetailStore,
|
viewIssues: projectViewIssueStore,
|
||||||
cycleIssue: cycleIssueStore,
|
workspaceProfileIssues: profileIssueStore,
|
||||||
moduleIssue: moduleIssueStore,
|
cycleIssues: cycleIssueStore,
|
||||||
|
moduleIssues: moduleIssueStore,
|
||||||
user: userStore,
|
user: userStore,
|
||||||
trackEvent: { postHogEventTracker }
|
trackEvent: { postHogEventTracker },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const user = userStore.currentUser;
|
const user = userStore.currentUser;
|
||||||
|
|
||||||
|
const issueStores = {
|
||||||
|
[EProjectStore.PROJECT]: {
|
||||||
|
store: projectIssueStore,
|
||||||
|
dataIdToUpdate: activeProject,
|
||||||
|
viewId: undefined,
|
||||||
|
},
|
||||||
|
[EProjectStore.PROJECT_VIEW]: {
|
||||||
|
store: projectViewIssueStore,
|
||||||
|
dataIdToUpdate: activeProject,
|
||||||
|
viewId: undefined,
|
||||||
|
},
|
||||||
|
[EProjectStore.PROFILE]: {
|
||||||
|
store: profileIssueStore,
|
||||||
|
dataIdToUpdate: user?.id || undefined,
|
||||||
|
viewId: undefined,
|
||||||
|
},
|
||||||
|
[EProjectStore.CYCLE]: {
|
||||||
|
store: cycleIssueStore,
|
||||||
|
dataIdToUpdate: activeProject,
|
||||||
|
viewId: cycleId,
|
||||||
|
},
|
||||||
|
[EProjectStore.MODULE]: {
|
||||||
|
store: moduleIssueStore,
|
||||||
|
dataIdToUpdate: activeProject,
|
||||||
|
viewId: moduleId,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { store: currentIssueStore, viewId, dataIdToUpdate } = issueStores[currentStore];
|
||||||
|
|
||||||
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined;
|
||||||
|
|
||||||
const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } = useLocalStorage<any>(
|
const { setValue: setValueInLocalStorage, clearValue: clearLocalStorageValue } = useLocalStorage<any>(
|
||||||
@ -176,60 +215,57 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
// in the url. This has the least priority.
|
// in the url. This has the least priority.
|
||||||
if (projects && projects.length > 0 && !activeProject)
|
if (projects && projects.length > 0 && !activeProject)
|
||||||
setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null);
|
setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null);
|
||||||
}, [activeProject, data, projectId, projects, isOpen]);
|
}, [data, projectId, projects, isOpen]);
|
||||||
|
|
||||||
const addIssueToCycle = async (issueId: string, cycleId: string) => {
|
const addIssueToCycle = async (issue: IIssue, cycleId: string) => {
|
||||||
if (!workspaceSlug || !activeProject) return;
|
if (!workspaceSlug || !activeProject) return;
|
||||||
|
|
||||||
cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), activeProject, cycleId, [issueId]);
|
cycleIssueStore.addIssueToCycle(workspaceSlug, activeProject, cycleId, issue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addIssueToModule = async (issueId: string, moduleId: string) => {
|
const addIssueToModule = async (issue: IIssue, moduleId: string) => {
|
||||||
if (!workspaceSlug || !activeProject) return;
|
if (!workspaceSlug || !activeProject) return;
|
||||||
|
|
||||||
moduleIssueStore.addIssueToModule(workspaceSlug.toString(), activeProject, moduleId, [issueId]);
|
moduleIssueStore.addIssueToModule(workspaceSlug, activeProject, moduleId, issue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createIssue = async (payload: Partial<IIssue>) => {
|
const createIssue = async (payload: Partial<IIssue>) => {
|
||||||
if (!workspaceSlug || !activeProject) return;
|
if (!workspaceSlug || !dataIdToUpdate) return;
|
||||||
|
|
||||||
await issueDetailStore
|
await currentIssueStore
|
||||||
.createIssue(workspaceSlug.toString(), activeProject, payload)
|
.createIssue(workspaceSlug, dataIdToUpdate, payload, viewId)
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
|
if (!res) throw new Error();
|
||||||
|
|
||||||
if (handleSubmit) {
|
if (handleSubmit) {
|
||||||
await handleSubmit(res);
|
await handleSubmit(res);
|
||||||
} else {
|
} else {
|
||||||
issueStore.fetchIssues(workspaceSlug.toString(), activeProject);
|
currentIssueStore.fetchIssues(workspaceSlug, dataIdToUpdate, "mutation");
|
||||||
|
|
||||||
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle);
|
if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res, payload.cycle);
|
||||||
if (payload.module && payload.module !== "") await addIssueToModule(res.id, payload.module);
|
if (payload.module && payload.module !== "") await addIssueToModule(res, payload.module);
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: "Issue created successfully.",
|
message: "Issue created successfully.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("ISSUE_CREATE", {
|
||||||
"ISSUE_CREATE",
|
...res,
|
||||||
{
|
state: "SUCCESS",
|
||||||
...res,
|
});
|
||||||
state: "SUCCESS"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent));
|
if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent));
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "Issue could not be created. Please try again.",
|
message: "Issue could not be created. Please try again.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("ISSUE_CREATE", {
|
||||||
"ISSUE_CREATE",
|
state: "FAILED",
|
||||||
{
|
});
|
||||||
state: "FAILED"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!createMore) onFormSubmitClose();
|
if (!createMore) onFormSubmitClose();
|
||||||
@ -269,10 +305,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateIssue = async (payload: Partial<IIssue>) => {
|
const updateIssue = async (payload: Partial<IIssue>) => {
|
||||||
if (!workspaceSlug || !activeProject || !data) return;
|
if (!workspaceSlug || !dataIdToUpdate || !data) return;
|
||||||
|
|
||||||
await issueDetailStore
|
await currentIssueStore
|
||||||
.updateIssue(workspaceSlug.toString(), activeProject, data.id, payload)
|
.updateIssue(workspaceSlug, dataIdToUpdate, data.id, payload, viewId)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (!createMore) onFormSubmitClose();
|
if (!createMore) onFormSubmitClose();
|
||||||
|
|
||||||
@ -281,13 +317,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
title: "Success!",
|
title: "Success!",
|
||||||
message: "Issue updated successfully.",
|
message: "Issue updated successfully.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("ISSUE_UPDATE", {
|
||||||
"ISSUE_UPDATE",
|
...res,
|
||||||
{
|
state: "SUCCESS",
|
||||||
...res,
|
});
|
||||||
state: "SUCCESS"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
@ -295,17 +328,14 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
|||||||
title: "Error!",
|
title: "Error!",
|
||||||
message: "Issue could not be updated. Please try again.",
|
message: "Issue could not be updated. Please try again.",
|
||||||
});
|
});
|
||||||
postHogEventTracker(
|
postHogEventTracker("ISSUE_UPDATE", {
|
||||||
"ISSUE_UPDATE",
|
state: "FAILED",
|
||||||
{
|
});
|
||||||
state: "FAILED"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<IIssue>) => {
|
const handleFormSubmit = async (formData: Partial<IIssue>) => {
|
||||||
if (!workspaceSlug || !activeProject) return;
|
if (!workspaceSlug || !dataIdToUpdate || !currentStore) return;
|
||||||
|
|
||||||
const payload: Partial<IIssue> = {
|
const payload: Partial<IIssue> = {
|
||||||
...formData,
|
...formData,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown, LayoutSelection } from "components/issues";
|
||||||
// hooks
|
// hooks
|
||||||
@ -6,40 +8,68 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
import { RootStore } from "store/root";
|
import { RootStore } from "store/root";
|
||||||
// constants
|
// constants
|
||||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||||
|
import { EFilterType } from "store/issues/types";
|
||||||
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||||
|
|
||||||
export const ProfileIssuesFilter = observer(() => {
|
export const ProfileIssuesFilter = observer(() => {
|
||||||
const { workspace: workspaceStore, profileIssueFilters: profileIssueFiltersStore }: RootStore = useMobxStore();
|
const router = useRouter();
|
||||||
|
const { workspaceSlug } = router.query as {
|
||||||
const handleLayoutChange = (_layout: string) => {
|
workspaceSlug: string;
|
||||||
const payload = {
|
|
||||||
layout: _layout,
|
|
||||||
group_by: profileIssueFiltersStore.userDisplayFilters.group_by
|
|
||||||
? profileIssueFiltersStore.userDisplayFilters.group_by
|
|
||||||
: "state_detail.group",
|
|
||||||
};
|
|
||||||
|
|
||||||
profileIssueFiltersStore.handleIssueFilters("userDisplayFilters", payload);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFilters = (key: any, value: any) => {
|
const {
|
||||||
let updatesFilters: any = profileIssueFiltersStore?.userFilters;
|
workspace: workspaceStore,
|
||||||
updatesFilters = updatesFilters[key] || [];
|
workspaceProfileIssuesFilter: { issueFilters, updateFilters },
|
||||||
if (updatesFilters && updatesFilters.length > 0 && updatesFilters.includes(value))
|
}: RootStore = useMobxStore();
|
||||||
updatesFilters = updatesFilters.filter((item: any) => item !== value);
|
|
||||||
else updatesFilters.push(value);
|
|
||||||
profileIssueFiltersStore.handleIssueFilters("userFilters", { [key]: updatesFilters });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDisplayFilters = (value: any) => profileIssueFiltersStore.handleIssueFilters("userDisplayFilters", value);
|
|
||||||
|
|
||||||
const handleDisplayProperties = (value: any) =>
|
|
||||||
profileIssueFiltersStore.handleIssueFilters("userDisplayProperties", value);
|
|
||||||
|
|
||||||
const states = undefined;
|
const states = undefined;
|
||||||
const labels = workspaceStore.workspaceLabels || undefined;
|
const labels = workspaceStore.workspaceLabels || undefined;
|
||||||
const members = undefined;
|
const members = undefined;
|
||||||
|
|
||||||
const activeLayout = profileIssueFiltersStore?.userDisplayFilters?.layout;
|
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
|
const handleLayoutChange = useCallback(
|
||||||
|
(layout: TIssueLayouts) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
updateFilters(workspaceSlug, EFilterType.DISPLAY_FILTERS, { layout: layout });
|
||||||
|
},
|
||||||
|
[workspaceSlug, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFiltersUpdate = useCallback(
|
||||||
|
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((val) => {
|
||||||
|
if (!newValues.includes(val)) newValues.push(val);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||||
|
else newValues.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFilters(workspaceSlug, EFilterType.FILTERS, { [key]: newValues });
|
||||||
|
},
|
||||||
|
[workspaceSlug, issueFilters, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDisplayFilters = useCallback(
|
||||||
|
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
updateFilters(workspaceSlug, EFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
||||||
|
},
|
||||||
|
[workspaceSlug, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDisplayProperties = useCallback(
|
||||||
|
(property: Partial<IIssueDisplayProperties>) => {
|
||||||
|
if (!workspaceSlug) return;
|
||||||
|
updateFilters(workspaceSlug, EFilterType.DISPLAY_PROPERTIES, property);
|
||||||
|
},
|
||||||
|
[workspaceSlug, updateFilters]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center justify-end gap-2">
|
<div className="relative flex items-center justify-end gap-2">
|
||||||
@ -51,11 +81,11 @@ export const ProfileIssuesFilter = observer(() => {
|
|||||||
|
|
||||||
<FiltersDropdown title="Filters" placement="bottom-end">
|
<FiltersDropdown title="Filters" placement="bottom-end">
|
||||||
<FilterSelection
|
<FilterSelection
|
||||||
filters={profileIssueFiltersStore.userFilters}
|
|
||||||
handleFiltersUpdate={handleFilters}
|
|
||||||
layoutDisplayFiltersOptions={
|
layoutDisplayFiltersOptions={
|
||||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
|
||||||
}
|
}
|
||||||
|
filters={issueFilters?.filters ?? {}}
|
||||||
|
handleFiltersUpdate={handleFiltersUpdate}
|
||||||
states={states}
|
states={states}
|
||||||
labels={labels}
|
labels={labels}
|
||||||
members={members}
|
members={members}
|
||||||
@ -64,13 +94,13 @@ export const ProfileIssuesFilter = observer(() => {
|
|||||||
|
|
||||||
<FiltersDropdown title="Display" placement="bottom-end">
|
<FiltersDropdown title="Display" placement="bottom-end">
|
||||||
<DisplayFiltersSelection
|
<DisplayFiltersSelection
|
||||||
displayFilters={profileIssueFiltersStore.userDisplayFilters}
|
|
||||||
displayProperties={profileIssueFiltersStore.userDisplayProperties}
|
|
||||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
|
||||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
|
||||||
layoutDisplayFiltersOptions={
|
layoutDisplayFiltersOptions={
|
||||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
|
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.profile_issues[activeLayout] : undefined
|
||||||
}
|
}
|
||||||
|
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||||
|
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||||
|
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||||
|
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||||
/>
|
/>
|
||||||
</FiltersDropdown>
|
</FiltersDropdown>
|
||||||
</div>
|
</div>
|
||||||
|
63
web/components/profile/profile-issues.tsx
Normal file
63
web/components/profile/profile-issues.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React, { ReactElement } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// layouts
|
||||||
|
import { AppLayout } from "layouts/app-layout";
|
||||||
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
|
// components
|
||||||
|
import { UserProfileHeader } from "components/headers";
|
||||||
|
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
||||||
|
import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
||||||
|
import { ProfileIssuesAppliedFiltersRoot } from "components/issues";
|
||||||
|
import { Spinner } from "@plane/ui";
|
||||||
|
// hooks
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
interface IProfileIssuesPage {
|
||||||
|
type: "assigned" | "subscribed" | "created";
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, userId } = router.query as {
|
||||||
|
workspaceSlug: string;
|
||||||
|
userId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
workspaceProfileIssues: { loader, getIssues, fetchIssues },
|
||||||
|
workspaceProfileIssuesFilter: { issueFilters, fetchFilters },
|
||||||
|
}: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
useSWR(workspaceSlug && userId ? `CURRENT_WORKSPACE_PROFILE_ISSUES_${workspaceSlug}_${userId}` : null, async () => {
|
||||||
|
if (workspaceSlug && userId) {
|
||||||
|
await fetchFilters(workspaceSlug);
|
||||||
|
await fetchIssues(workspaceSlug, userId, getIssues ? "mutation" : "init-loader", props.type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeLayout = issueFilters?.displayFilters?.layout || undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{loader === "init-loader" ? (
|
||||||
|
<div className="flex justify-center items-center w-full h-full">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ProfileIssuesAppliedFiltersRoot />
|
||||||
|
<div className="w-full h-full relative overflow-auto -z-1">
|
||||||
|
{activeLayout === "list" ? (
|
||||||
|
<ProfileIssuesListLayout />
|
||||||
|
) : activeLayout === "kanban" ? (
|
||||||
|
<ProfileIssuesKanBanLayout />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -9,12 +9,17 @@ import { CreateUpdateDraftIssueModal } from "components/issues";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { EProjectStore } from "store/command-palette.store";
|
||||||
|
|
||||||
export const WorkspaceSidebarQuickAction = observer(() => {
|
export const WorkspaceSidebarQuickAction = observer(() => {
|
||||||
// states
|
// states
|
||||||
const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false);
|
const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false);
|
||||||
|
|
||||||
const { theme: themeStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore();
|
const {
|
||||||
|
theme: themeStore,
|
||||||
|
commandPalette: commandPaletteStore,
|
||||||
|
trackEvent: { setTrackElement },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const { storedValue, clearValue } = useLocalStorage<any>("draftedIssue", JSON.stringify({}));
|
const { storedValue, clearValue } = useLocalStorage<any>("draftedIssue", JSON.stringify({}));
|
||||||
|
|
||||||
@ -34,24 +39,26 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-between w-full cursor-pointer px-4 mt-4 ${isSidebarCollapsed ? "flex-col gap-1" : "gap-2"
|
className={`flex items-center justify-between w-full cursor-pointer px-4 mt-4 ${
|
||||||
}`}
|
isSidebarCollapsed ? "flex-col gap-1" : "gap-2"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`relative flex items-center justify-between w-full rounded cursor-pointer px-2 gap-1 group ${isSidebarCollapsed
|
className={`relative flex items-center justify-between w-full rounded cursor-pointer px-2 gap-1 group ${
|
||||||
? "px-2 hover:bg-custom-sidebar-background-80"
|
isSidebarCollapsed
|
||||||
: "px-3 shadow-custom-sidebar-shadow-2xs border-[0.5px] border-custom-border-200"
|
? "px-2 hover:bg-custom-sidebar-background-80"
|
||||||
}`}
|
: "px-3 shadow-custom-sidebar-shadow-2xs border-[0.5px] border-custom-border-200"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5 outline-none ${isSidebarCollapsed ? "justify-center" : ""
|
className={`relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5 outline-none ${
|
||||||
}`}
|
isSidebarCollapsed ? "justify-center" : ""
|
||||||
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
||||||
commandPaletteStore.toggleCreateIssueModal(true);
|
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT);
|
||||||
}
|
}}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<PenSquare className="h-4 w-4 text-custom-sidebar-text-300" />
|
<PenSquare className="h-4 w-4 text-custom-sidebar-text-300" />
|
||||||
{!isSidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
|
{!isSidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
|
||||||
@ -63,8 +70,9 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex items-center justify-center rounded flex-shrink-0 py-1.5 ml-1.5 ${isSidebarCollapsed ? "hidden" : "block"
|
className={`flex items-center justify-center rounded flex-shrink-0 py-1.5 ml-1.5 ${
|
||||||
}`}
|
isSidebarCollapsed ? "hidden" : "block"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={16}
|
size={16}
|
||||||
@ -73,8 +81,9 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`fixed h-10 pt-2 w-[203px] left-4 opacity-0 group-hover:opacity-100 mt-0 pointer-events-none group-hover:pointer-events-auto ${isSidebarCollapsed ? "top-[5.5rem]" : "top-24"
|
className={`fixed h-10 pt-2 w-[203px] left-4 opacity-0 group-hover:opacity-100 mt-0 pointer-events-none group-hover:pointer-events-auto ${
|
||||||
}`}
|
isSidebarCollapsed ? "top-[5.5rem]" : "top-24"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<button
|
<button
|
||||||
@ -91,10 +100,11 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className={`flex items-center justify-center rounded flex-shrink-0 p-2 outline-none ${isSidebarCollapsed
|
className={`flex items-center justify-center rounded flex-shrink-0 p-2 outline-none ${
|
||||||
? "hover:bg-custom-sidebar-background-80"
|
isSidebarCollapsed
|
||||||
: "shadow-custom-sidebar-shadow-2xs border-[0.5px] border-custom-border-200"
|
? "hover:bg-custom-sidebar-background-80"
|
||||||
}`}
|
: "shadow-custom-sidebar-shadow-2xs border-[0.5px] border-custom-border-200"
|
||||||
|
}`}
|
||||||
onClick={() => commandPaletteStore.toggleCommandPaletteModal(true)}
|
onClick={() => commandPaletteStore.toggleCommandPaletteModal(true)}
|
||||||
>
|
>
|
||||||
<Search className="h-4 w-4 text-custom-sidebar-text-300" />
|
<Search className="h-4 w-4 text-custom-sidebar-text-300" />
|
||||||
|
@ -1,67 +1,15 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import useSWR from "swr";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
|
||||||
import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
|
||||||
import { Spinner } from "@plane/ui";
|
|
||||||
// hooks
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
import { RootStore } from "store/root";
|
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "types/app";
|
import { NextPageWithLayout } from "types/app";
|
||||||
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileAssignedIssuesPage: NextPageWithLayout = observer(() => {
|
const ProfileAssignedIssuesPage: NextPageWithLayout = observer(() => <ProfileIssuesPage type="assigned" />);
|
||||||
const {
|
|
||||||
workspace: workspaceStore,
|
|
||||||
project: projectStore,
|
|
||||||
profileIssueFilters: profileIssueFiltersStore,
|
|
||||||
profileIssues: profileIssuesStore,
|
|
||||||
}: RootStore = useMobxStore();
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, userId } = router.query as {
|
|
||||||
workspaceSlug: string;
|
|
||||||
userId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const { isLoading } = useSWR(`PROFILE_ISSUES_${workspaceSlug}_${userId}`, async () => {
|
|
||||||
if (workspaceSlug && userId) {
|
|
||||||
// workspace labels
|
|
||||||
workspaceStore.setWorkspaceSlug(workspaceSlug);
|
|
||||||
await workspaceStore.fetchWorkspaceLabels(workspaceSlug);
|
|
||||||
await projectStore.fetchProjects(workspaceSlug);
|
|
||||||
|
|
||||||
//profile issues
|
|
||||||
await profileIssuesStore.fetchIssues(workspaceSlug, userId, "assigned");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeLayout = profileIssueFiltersStore.userDisplayFilters.layout;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-full h-full relative overflow-auto -z-1">
|
|
||||||
{activeLayout === "list" ? (
|
|
||||||
<ProfileIssuesListLayout />
|
|
||||||
) : activeLayout === "kanban" ? (
|
|
||||||
<ProfileIssuesKanBanLayout />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,63 +1,16 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import useSWR from "swr";
|
|
||||||
// store
|
// store
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
|
||||||
import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
|
||||||
import { Spinner } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "types/app";
|
import { NextPageWithLayout } from "types/app";
|
||||||
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileCreatedIssuesPage: NextPageWithLayout = () => {
|
const ProfileCreatedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="created" />;
|
||||||
const {
|
|
||||||
workspace: workspaceStore,
|
|
||||||
project: projectStore,
|
|
||||||
profileIssueFilters: profileIssueFiltersStore,
|
|
||||||
profileIssues: profileIssuesStore,
|
|
||||||
} = useMobxStore();
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, userId } = router.query;
|
|
||||||
|
|
||||||
const { isLoading } = useSWR(`PROFILE_ISSUES_CREATED_${workspaceSlug}_${userId}`, async () => {
|
|
||||||
if (workspaceSlug && userId) {
|
|
||||||
// workspace labels
|
|
||||||
workspaceStore.setWorkspaceSlug(workspaceSlug.toString());
|
|
||||||
await workspaceStore.fetchWorkspaceLabels(workspaceSlug.toString());
|
|
||||||
await projectStore.fetchProjects(workspaceSlug.toString());
|
|
||||||
|
|
||||||
//profile issues
|
|
||||||
await profileIssuesStore.fetchIssues(workspaceSlug.toString(), userId.toString(), "created");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeLayout = profileIssueFiltersStore.userDisplayFilters.layout;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-full h-full relative overflow-auto -z-1">
|
|
||||||
{activeLayout === "list" ? (
|
|
||||||
<ProfileIssuesListLayout />
|
|
||||||
) : activeLayout === "kanban" ? (
|
|
||||||
<ProfileIssuesKanBanLayout />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,63 +1,16 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import useSWR from "swr";
|
|
||||||
// store
|
// store
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
|
||||||
import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
|
||||||
import { Spinner } from "@plane/ui";
|
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "types/app";
|
import { NextPageWithLayout } from "types/app";
|
||||||
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileSubscribedIssuesPage: NextPageWithLayout = () => {
|
const ProfileSubscribedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="subscribed" />;
|
||||||
const {
|
|
||||||
workspace: workspaceStore,
|
|
||||||
project: projectStore,
|
|
||||||
profileIssueFilters: profileIssueFiltersStore,
|
|
||||||
profileIssues: profileIssuesStore,
|
|
||||||
} = useMobxStore();
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug, userId } = router.query;
|
|
||||||
|
|
||||||
const { isLoading } = useSWR(`PROFILE_ISSUES_SUBSCRIBED_${workspaceSlug}_${userId}`, async () => {
|
|
||||||
if (workspaceSlug && userId) {
|
|
||||||
// workspace labels
|
|
||||||
workspaceStore.setWorkspaceSlug(workspaceSlug.toString());
|
|
||||||
await workspaceStore.fetchWorkspaceLabels(workspaceSlug.toString());
|
|
||||||
await projectStore.fetchProjects(workspaceSlug.toString());
|
|
||||||
|
|
||||||
//profile issues
|
|
||||||
await profileIssuesStore.fetchIssues(workspaceSlug.toString(), userId.toString(), "subscribed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeLayout = profileIssueFiltersStore.userDisplayFilters.layout;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{isLoading ? (
|
|
||||||
<div className="flex justify-center items-center w-full h-full">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-full h-full relative overflow-auto -z-1">
|
|
||||||
{activeLayout === "list" ? (
|
|
||||||
<ProfileIssuesListLayout />
|
|
||||||
) : activeLayout === "kanban" ? (
|
|
||||||
<ProfileIssuesKanBanLayout />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,7 @@ import { ProjectDraftIssueHeader } from "components/headers";
|
|||||||
import { X, PenSquare } from "lucide-react";
|
import { X, PenSquare } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "types/app";
|
import { NextPageWithLayout } from "types/app";
|
||||||
|
import { DraftIssueLayoutRoot } from "components/issues/issue-layouts/roots/draft-issue-layout-root";
|
||||||
|
|
||||||
const ProjectDraftIssuesPage: NextPageWithLayout = () => {
|
const ProjectDraftIssuesPage: NextPageWithLayout = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -21,14 +22,14 @@ const ProjectDraftIssuesPage: NextPageWithLayout = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues/`)}
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues/`)}
|
||||||
className="flex items-center gap-1.5 rounded border border-custom-border-200 px-3 py-1.5 text-xs"
|
className="flex items-center gap-1.5 rounded-full border border-custom-border-200 px-3 py-1.5 text-xs"
|
||||||
>
|
>
|
||||||
<PenSquare className="h-3 w-3 text-custom-text-300" />
|
<PenSquare className="h-4 w-4" />
|
||||||
<span>Draft Issues</span>
|
<span>Draft Issues</span>
|
||||||
|
|
||||||
<X className="h-3 w-3" />
|
<X className="h-3 w-3" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<DraftIssueLayoutRoot />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -18,8 +18,8 @@ export class IssueArchiveService extends APIService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getV3ArchivedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<any> {
|
async getV3ArchivedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<any> {
|
||||||
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/archived-issues/`, {
|
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, {
|
||||||
params: queries,
|
params: { ...queries, archived: true },
|
||||||
})
|
})
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -17,13 +17,13 @@ export class IssueDraftService extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getV3DraftIssues(workspaceSlug: string, projectId: string, params?: any): Promise<any> {
|
async getV3DraftIssues(workspaceSlug: string, projectId: string, query?: any): Promise<any> {
|
||||||
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, {
|
return this.get(`/api/v3/workspaces/${workspaceSlug}/projects/${projectId}/issues/?draft=true`, {
|
||||||
params,
|
params: { ...query },
|
||||||
})
|
})
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw error?.response;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user