forked from github/plane
refactor: draft issue filter store
This commit is contained in:
parent
855c65bc87
commit
f9586ede31
@ -19,3 +19,4 @@ export * from "./project-archived-issue-details";
|
||||
export * from "./project-archived-issues";
|
||||
export * from "./project-issue-details";
|
||||
export * from "./user-profile";
|
||||
export * from "./project-draft-issues";
|
||||
|
@ -3,18 +3,73 @@ import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||
// ui
|
||||
import { Breadcrumbs, LayersIcon } from "@plane/ui";
|
||||
// types
|
||||
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "types";
|
||||
// constants
|
||||
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||
// helper
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
|
||||
export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { project: projectStore } = useMobxStore();
|
||||
const { project: projectStore, draftIssueFilters: draftIssueFiltersStore } = useMobxStore();
|
||||
const { currentProjectDetails } = projectStore;
|
||||
|
||||
const activeLayout = draftIssueFiltersStore.userDisplayFilters.layout;
|
||||
|
||||
const handleLayoutChange = (layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
display_filters: {
|
||||
layout,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleFiltersUpdate = (key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const newValues = draftIssueFiltersStore.userFilters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
if (!newValues.includes(val)) newValues.push(val);
|
||||
});
|
||||
} else {
|
||||
if (draftIssueFiltersStore.userFilters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
filters: {
|
||||
[key]: newValues,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDisplayFiltersUpdate = (updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
display_filters: {
|
||||
...updatedDisplayFilter,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleDisplayPropertiesUpdate = (property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
draftIssueFiltersStore.updateDisplayProperties(workspaceSlug.toString(), projectId.toString(), property);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative flex w-full flex-shrink-0 flex-row z-10 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">
|
||||
@ -45,6 +100,38 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* filter/layout/display options */}
|
||||
<div className="flex items-center gap-2">
|
||||
<LayoutSelection
|
||||
layouts={["list", "kanban"]}
|
||||
onChange={(layout) => handleLayoutChange(layout)}
|
||||
selectedLayout={activeLayout}
|
||||
/>
|
||||
<FiltersDropdown title="Filters" placement="bottom-end">
|
||||
<FilterSelection
|
||||
filters={draftIssueFiltersStore.userFilters}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.draft_issues[activeLayout] : undefined
|
||||
}
|
||||
labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? undefined}
|
||||
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
||||
states={projectStore.states?.[projectId?.toString() ?? ""] ?? undefined}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
<FiltersDropdown title="Display" placement="bottom-end">
|
||||
<DisplayFiltersSelection
|
||||
displayFilters={draftIssueFiltersStore.userDisplayFilters}
|
||||
displayProperties={draftIssueFiltersStore.userDisplayProperties}
|
||||
handleDisplayFiltersUpdate={handleDisplayFiltersUpdate}
|
||||
handleDisplayPropertiesUpdate={handleDisplayPropertiesUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.draft_issues[activeLayout] : undefined
|
||||
}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -22,7 +22,6 @@ import {
|
||||
import { CreateStateModal } from "components/states";
|
||||
import { CreateLabelModal } from "components/labels";
|
||||
// ui
|
||||
import {} from "components/ui";
|
||||
import { Button, CustomMenu, Input, ToggleSwitch } from "@plane/ui";
|
||||
// icons
|
||||
import { Sparkle, X } from "lucide-react";
|
||||
@ -122,7 +121,7 @@ export const DraftIssueForm: FC<IssueFormProps> = (props) => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId)
|
||||
const editorSuggestions = useEditorSuggestions(workspaceSlug as string | undefined, projectId);
|
||||
|
||||
const {
|
||||
formState: { errors, isSubmitting },
|
||||
|
@ -4,3 +4,4 @@ export * from "./module-root";
|
||||
export * from "./project-view-root";
|
||||
export * from "./project-root";
|
||||
export * from "./archived-issue";
|
||||
export * from "./project-draft-issue";
|
||||
|
@ -0,0 +1,81 @@
|
||||
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";
|
||||
|
||||
export const ProjectDraftIssueAppliedFiltersRoot: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { project: projectStore, draftIssueFilters: draftIssueFiltersStore } = useMobxStore();
|
||||
|
||||
const userFilters = draftIssueFiltersStore.userFilters;
|
||||
|
||||
// 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) {
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
filters: {
|
||||
[key]: null,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the passed value from the key
|
||||
let newValues = draftIssueFiltersStore.userFilters?.[key] ?? [];
|
||||
newValues = newValues.filter((val) => val !== value);
|
||||
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
filters: {
|
||||
[key]: newValues,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleClearAllFilters = () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const newFilters: IIssueFilterOptions = {};
|
||||
Object.keys(userFilters).forEach((key) => {
|
||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||
});
|
||||
|
||||
draftIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
|
||||
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={projectStore.labels?.[projectId?.toString() ?? ""] ?? []}
|
||||
members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)}
|
||||
states={projectStore.states?.[projectId?.toString() ?? ""]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
@ -8,8 +8,6 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { KanBanSwimLanes } from "../swimlanes";
|
||||
import { KanBan } from "../default";
|
||||
import { DraftIssueQuickActions } from "components/issues";
|
||||
// helpers
|
||||
import { orderArrayBy } from "helpers/array.helper";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
// constants
|
||||
@ -17,27 +15,24 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
||||
|
||||
export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const {
|
||||
project: projectStore,
|
||||
issueFilter: issueFilterStore,
|
||||
draftIssueFilters: draftIssueFiltersStore,
|
||||
issueKanBanView: issueKanBanViewStore,
|
||||
draftIssues: draftIssuesStore,
|
||||
} = useMobxStore();
|
||||
|
||||
// derived values
|
||||
const issues = draftIssuesStore.getDraftIssues;
|
||||
|
||||
const sub_group_by: string | null = issueFilterStore?.userDisplayFilters?.sub_group_by || null;
|
||||
|
||||
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
|
||||
|
||||
const display_properties = issueFilterStore?.userDisplayProperties || null;
|
||||
const display_properties = draftIssueFiltersStore?.userDisplayProperties;
|
||||
const userDisplayFilters = draftIssueFiltersStore?.userDisplayFilters;
|
||||
const group_by: string | null = userDisplayFilters?.group_by || null;
|
||||
const showEmptyGroup = userDisplayFilters?.show_empty_groups || false;
|
||||
const sub_group_by: string | null = userDisplayFilters?.sub_group_by || null;
|
||||
|
||||
const currentKanBanView = "default";
|
||||
// const currentKanBanView: "swimlanes" | "default" = issueFilterStore?.userDisplayFilters?.sub_group_by
|
||||
// ? "swimlanes"
|
||||
// : "default";
|
||||
|
||||
const onDragEnd = (result: any) => {
|
||||
if (!result) return;
|
||||
@ -50,7 +45,6 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
)
|
||||
return;
|
||||
|
||||
// TODO: use draft issue store instead
|
||||
currentKanBanView === "default"
|
||||
? issueKanBanViewStore?.handleDragDrop(result.source, result.destination)
|
||||
: issueKanBanViewStore?.handleSwimlaneDragDrop(result.source, result.destination);
|
||||
@ -65,18 +59,14 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
const grouping = {
|
||||
group_id: group_by,
|
||||
sub_group_id: sub_group_by,
|
||||
};
|
||||
|
||||
if (action === "update") {
|
||||
draftIssuesStore.updateDraftIssue(workspaceSlug.toString(), issue.project, issue);
|
||||
draftIssuesStore.updateIssueStructure(group_by, sub_group_by, issue);
|
||||
draftIssuesStore.updateDraftIssue(workspaceSlug.toString(), issue.project, grouping, issue);
|
||||
}
|
||||
if (action === "delete") draftIssuesStore.deleteDraftIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
if (action === "convertToIssue")
|
||||
draftIssuesStore.convertDraftIssueToIssue(workspaceSlug.toString(), issue.project, grouping, issue.id);
|
||||
draftIssuesStore.convertDraftIssueToIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
draftIssuesStore.fetchIssues(workspaceSlug.toString(), issue.project);
|
||||
},
|
||||
[draftIssuesStore, workspaceSlug]
|
||||
);
|
||||
@ -85,18 +75,12 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
issueKanBanViewStore.handleKanBanToggle(toggle, value);
|
||||
};
|
||||
|
||||
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
|
||||
|
||||
const states = projectStore?.projectStates || null;
|
||||
const priorities = ISSUE_PRIORITIES || null;
|
||||
const labels = projectStore?.projectLabels || null;
|
||||
const members = projectStore?.projectMembers || null;
|
||||
const stateGroups = ISSUE_STATE_GROUPS || null;
|
||||
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
|
||||
const estimates =
|
||||
projectDetails?.estimate !== null
|
||||
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
|
||||
@ -113,7 +97,7 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
handleUpdate={(issue: any, action: any) => handleIssues(sub_group_by, group_by, issue, action)}
|
||||
/>
|
||||
)}
|
||||
display_properties={display_properties}
|
||||
displayProperties={display_properties}
|
||||
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
|
||||
handleKanBanToggle={handleKanBanToggle}
|
||||
states={states}
|
||||
@ -122,7 +106,7 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
labels={labels}
|
||||
members={members?.map((m) => m.member) ?? null}
|
||||
projects={projects}
|
||||
estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
/>
|
||||
) : (
|
||||
<KanBanSwimLanes
|
||||
@ -136,7 +120,7 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
handleUpdate={(issue: any, action: any) => handleIssues(sub_group_by, group_by, issue, action)}
|
||||
/>
|
||||
)}
|
||||
display_properties={display_properties}
|
||||
displayProperties={display_properties}
|
||||
kanBanToggle={issueKanBanViewStore?.kanBanToggle}
|
||||
handleKanBanToggle={handleKanBanToggle}
|
||||
states={states}
|
||||
@ -145,7 +129,7 @@ export const DraftIssueKanBanLayout: React.FC = observer(() => {
|
||||
labels={labels}
|
||||
members={members?.map((m) => m.member) ?? null}
|
||||
projects={projects}
|
||||
estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
/>
|
||||
)}
|
||||
</DragDropContext>
|
||||
|
@ -17,13 +17,17 @@ export const DraftIssueListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { project: projectStore, draftIssues: draftIssuesStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
const {
|
||||
project: projectStore,
|
||||
draftIssues: draftIssuesStore,
|
||||
draftIssueFilters: draftIssueFiltersStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const issues = draftIssuesStore.getDraftIssues;
|
||||
|
||||
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
|
||||
const group_by: string | null = draftIssueFiltersStore?.userDisplayFilters?.group_by || null;
|
||||
|
||||
const display_properties = issueFilterStore?.userDisplayProperties || null;
|
||||
const display_properties = draftIssueFiltersStore?.userDisplayProperties || null;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
(group_by: string | null, issue: IIssue, action: "update" | "delete" | "convertToIssue") => {
|
||||
|
@ -5,26 +5,26 @@ import useSWR from "swr";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { DraftIssueListLayout, DraftIssueKanBanLayout, ProjectAppliedFiltersRoot } from "components/issues";
|
||||
import { DraftIssueListLayout, DraftIssueKanBanLayout, ProjectDraftIssueAppliedFiltersRoot } from "components/issues";
|
||||
|
||||
export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { issueFilter: issueFilterStore, draftIssues: draftIssuesStore } = useMobxStore();
|
||||
const { draftIssues: draftIssuesStore, draftIssueFilters: draftIssueFiltersStore } = useMobxStore();
|
||||
|
||||
useSWR(workspaceSlug && projectId ? `PROJECT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => {
|
||||
if (workspaceSlug && projectId) {
|
||||
await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||
await draftIssueFiltersStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString());
|
||||
await draftIssuesStore.fetchIssues(workspaceSlug.toString(), projectId.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const activeLayout = issueFilterStore.userDisplayFilters.layout;
|
||||
const activeLayout = draftIssueFiltersStore.userDisplayFilters.layout;
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full flex flex-col overflow-hidden">
|
||||
<ProjectAppliedFiltersRoot />
|
||||
<ProjectDraftIssueAppliedFiltersRoot />
|
||||
<div className="w-full h-full overflow-auto">
|
||||
{activeLayout === "list" ? (
|
||||
<DraftIssueListLayout />
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
||||
import { observable, action, computed, makeObservable, runInAction, autorun } from "mobx";
|
||||
// store
|
||||
import { RootStore } from "../root";
|
||||
// types
|
||||
@ -41,24 +41,8 @@ export interface IIssueDraftStore {
|
||||
createDraftIssue: (workspaceSlug: string, projectId: string, issueForm: Partial<IIssue>) => Promise<any>;
|
||||
updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void;
|
||||
deleteDraftIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<any>;
|
||||
updateDraftIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
grouping: {
|
||||
group_id: string | null;
|
||||
sub_group_id: string | null;
|
||||
},
|
||||
issueForm: Partial<IIssue>
|
||||
) => Promise<any>;
|
||||
convertDraftIssueToIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
grouping: {
|
||||
group_id: string | null;
|
||||
sub_group_id: string | null;
|
||||
},
|
||||
issueId: string
|
||||
) => Promise<any>;
|
||||
updateDraftIssue: (workspaceSlug: string, projectId: string, issueForm: Partial<IIssue>) => Promise<any>;
|
||||
convertDraftIssueToIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<any>;
|
||||
|
||||
// service
|
||||
draftIssueService: IssueDraftService;
|
||||
@ -91,18 +75,29 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
});
|
||||
this.rootStore = _rootStore;
|
||||
this.draftIssueService = new IssueDraftService();
|
||||
|
||||
autorun(() => {
|
||||
const workspaceSlug = this.rootStore.workspace.workspaceSlug;
|
||||
const projectId = this.rootStore.project.projectId;
|
||||
|
||||
if (
|
||||
workspaceSlug &&
|
||||
projectId &&
|
||||
this.rootStore.draftIssueFilters.userFilters &&
|
||||
this.rootStore.draftIssueFilters.userDisplayFilters
|
||||
)
|
||||
this.fetchIssues(workspaceSlug, projectId);
|
||||
});
|
||||
}
|
||||
|
||||
get getIssueType() {
|
||||
// FIXME: this is temporary for development
|
||||
// return "grouped";
|
||||
return "ungrouped";
|
||||
|
||||
// TODO: fix this such that we only have the
|
||||
// conditions for layouts that are actually allowed in draft issues
|
||||
const groupedLayouts = ["kanban", "list", "calendar"];
|
||||
const ungroupedLayouts = ["spreadsheet", "gantt_chart"];
|
||||
|
||||
const issueLayout = this.rootStore?.issueFilter?.userDisplayFilters?.layout || null;
|
||||
const issueSubGroup = this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by || null;
|
||||
const issueLayout = this.rootStore?.draftIssueFilters?.userDisplayFilters?.layout || null;
|
||||
const issueSubGroup = this.rootStore?.draftIssueFilters?.userDisplayFilters?.sub_group_by || null;
|
||||
if (!issueLayout) return null;
|
||||
|
||||
const _issueState = groupedLayouts.includes(issueLayout)
|
||||
@ -119,6 +114,7 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
get getDraftIssues() {
|
||||
const issueType = this.getIssueType;
|
||||
const projectId = this.rootStore?.project?.projectId;
|
||||
|
||||
if (!projectId || !issueType) return null;
|
||||
|
||||
return this.draftIssues?.[projectId]?.[issueType] || null;
|
||||
@ -132,11 +128,7 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
this.rootStore.workspace.setWorkspaceSlug(workspaceSlug);
|
||||
this.rootStore.project.setProjectId(projectId);
|
||||
|
||||
// const params = this.rootStore?.issueFilter?.appliedFilters;
|
||||
// TODO: use actual params using applied filters
|
||||
const params = {
|
||||
// group_by: "state",
|
||||
};
|
||||
const params = this.rootStore?.draftIssueFilters?.appliedFilters;
|
||||
const issueResponse = await this.draftIssueService.getDraftIssues(workspaceSlug, projectId, params);
|
||||
|
||||
const issueType = this.getIssueType;
|
||||
@ -246,7 +238,7 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
else issues = [...issues, issue];
|
||||
}
|
||||
|
||||
const orderBy = this.rootStore?.issueFilter?.userDisplayFilters?.order_by || "";
|
||||
const orderBy = this.rootStore?.draftIssueFilters?.userDisplayFilters?.order_by || "";
|
||||
if (orderBy === "-created_at") {
|
||||
issues = sortArrayByDate(issues as any, "created_at");
|
||||
}
|
||||
@ -265,27 +257,14 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
});
|
||||
};
|
||||
|
||||
updateDraftIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
grouping: {
|
||||
group_id: string | null;
|
||||
sub_group_id: string | null;
|
||||
},
|
||||
issueForm: Partial<IIssue>
|
||||
) => {
|
||||
updateDraftIssue = async (workspaceSlug: string, projectId: string, issueForm: Partial<IIssue>) => {
|
||||
const originalIssues = { ...this.draftIssues };
|
||||
|
||||
const { group_id, sub_group_id } = grouping;
|
||||
|
||||
runInAction(() => {
|
||||
this.loader = true;
|
||||
this.error = null;
|
||||
});
|
||||
|
||||
// optimistic updating draft issue
|
||||
this.updateIssueStructure(group_id, sub_group_id, issueForm as IIssue);
|
||||
|
||||
try {
|
||||
const response = await this.draftIssueService.updateDraftIssue(
|
||||
workspaceSlug,
|
||||
@ -310,17 +289,9 @@ export class IssueDraftStore implements IIssueDraftStore {
|
||||
}
|
||||
};
|
||||
|
||||
convertDraftIssueToIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
grouping: {
|
||||
group_id: string | null;
|
||||
sub_group_id: string | null;
|
||||
},
|
||||
issueId: string
|
||||
) =>
|
||||
convertDraftIssueToIssue = async (workspaceSlug: string, projectId: string, issueId: string) =>
|
||||
// TODO: add removing item from draft issue list
|
||||
await this.updateDraftIssue(workspaceSlug, projectId, grouping, { id: issueId, is_draft: false });
|
||||
await this.updateDraftIssue(workspaceSlug, projectId, { id: issueId, is_draft: false });
|
||||
|
||||
deleteDraftIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||
const originalIssues = { ...this.draftIssues };
|
||||
|
@ -1,20 +1,53 @@
|
||||
import { observable, computed, makeObservable } from "mobx";
|
||||
import { observable, computed, makeObservable, runInAction, action } from "mobx";
|
||||
// helpers
|
||||
import { handleIssueQueryParamsByLayout } from "helpers/issue.helper";
|
||||
// services
|
||||
import { IssueService } from "services/issue";
|
||||
import { ProjectService } from "services/project";
|
||||
// types
|
||||
import { RootStore } from "../root";
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueParams } from "types";
|
||||
import type {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
IIssueFilterOptions,
|
||||
TIssueParams,
|
||||
IProjectViewProps,
|
||||
} from "types";
|
||||
|
||||
export interface IDraftIssueFilterStore {
|
||||
loader: boolean;
|
||||
error: any | null;
|
||||
|
||||
// observables
|
||||
userDisplayProperties: IIssueDisplayProperties;
|
||||
userDisplayFilters: IIssueDisplayFilterOptions;
|
||||
userFilters: IIssueFilterOptions;
|
||||
|
||||
// services
|
||||
projectService: ProjectService;
|
||||
issueService: IssueService;
|
||||
|
||||
// computed
|
||||
appliedFilters: TIssueParams[] | null;
|
||||
|
||||
// actions
|
||||
fetchUserProjectFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
updateUserFilters: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
properties: Partial<IProjectViewProps>
|
||||
) => Promise<void>;
|
||||
updateDisplayProperties: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
properties: Partial<IIssueDisplayProperties>
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export class DraftIssueFilterStore implements IDraftIssueFilterStore {
|
||||
loader: boolean = false;
|
||||
error: any | null = null;
|
||||
|
||||
// observables
|
||||
userFilters: IIssueFilterOptions = {
|
||||
priority: null,
|
||||
@ -52,8 +85,15 @@ export class DraftIssueFilterStore implements IDraftIssueFilterStore {
|
||||
// root store
|
||||
rootStore;
|
||||
|
||||
// services
|
||||
projectService: ProjectService;
|
||||
issueService: IssueService;
|
||||
|
||||
constructor(_rootStore: RootStore) {
|
||||
makeObservable(this, {
|
||||
loader: observable,
|
||||
error: observable,
|
||||
|
||||
// observables
|
||||
userFilters: observable.ref,
|
||||
userDisplayFilters: observable.ref,
|
||||
@ -61,9 +101,19 @@ export class DraftIssueFilterStore implements IDraftIssueFilterStore {
|
||||
|
||||
// computed
|
||||
appliedFilters: computed,
|
||||
|
||||
// actions
|
||||
fetchUserProjectFilters: action,
|
||||
updateUserFilters: action,
|
||||
updateDisplayProperties: action,
|
||||
computedFilter: action,
|
||||
});
|
||||
|
||||
this.rootStore = _rootStore;
|
||||
|
||||
// services
|
||||
this.issueService = new IssueService();
|
||||
this.projectService = new ProjectService();
|
||||
}
|
||||
|
||||
computedFilter = (filters: any, filteredParams: any) => {
|
||||
@ -98,12 +148,110 @@ export class DraftIssueFilterStore implements IDraftIssueFilterStore {
|
||||
start_target_date: this.userDisplayFilters?.start_target_date || true,
|
||||
};
|
||||
|
||||
// this.userDisplayFilters.layout should be list or kanban
|
||||
const filteredParams = handleIssueQueryParamsByLayout(this.userDisplayFilters.layout, "issues");
|
||||
if (filteredParams) filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams);
|
||||
|
||||
if (this.userDisplayFilters.layout === "calendar") filteredRouteParams.group_by = "target_date";
|
||||
if (this.userDisplayFilters.layout === "gantt_chart") filteredRouteParams.start_target_date = true;
|
||||
|
||||
return filteredRouteParams;
|
||||
}
|
||||
|
||||
updateUserFilters = async (workspaceSlug: string, projectId: string, properties: Partial<IProjectViewProps>) => {
|
||||
const newViewProps = {
|
||||
display_filters: {
|
||||
...this.userDisplayFilters,
|
||||
...properties.display_filters,
|
||||
},
|
||||
filters: {
|
||||
...this.userFilters,
|
||||
...properties.filters,
|
||||
},
|
||||
};
|
||||
|
||||
// set sub_group_by to null if group_by is set to null
|
||||
if (newViewProps.display_filters.group_by === null) newViewProps.display_filters.sub_group_by = null;
|
||||
|
||||
// set group_by to state if layout is switched to kanban and group_by is null
|
||||
if (newViewProps.display_filters.layout === "kanban" && newViewProps.display_filters.group_by === null)
|
||||
newViewProps.display_filters.group_by = "state";
|
||||
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.userFilters = newViewProps.filters;
|
||||
this.userDisplayFilters = {
|
||||
...newViewProps.display_filters,
|
||||
// set layout to list if layout is not list or kanban
|
||||
layout:
|
||||
newViewProps.display_filters.layout === "list" || newViewProps.display_filters.layout === "kanban"
|
||||
? newViewProps.display_filters.layout
|
||||
: "list",
|
||||
};
|
||||
});
|
||||
|
||||
this.projectService.setProjectView(workspaceSlug, projectId, {
|
||||
view_props: newViewProps,
|
||||
});
|
||||
} catch (error) {
|
||||
this.fetchUserProjectFilters(workspaceSlug, projectId);
|
||||
|
||||
runInAction(() => {
|
||||
this.error = error;
|
||||
});
|
||||
|
||||
console.log("Failed to update user filters in issue filter store", error);
|
||||
}
|
||||
};
|
||||
|
||||
updateDisplayProperties = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
properties: Partial<IIssueDisplayProperties>
|
||||
) => {
|
||||
const newProperties: IIssueDisplayProperties = {
|
||||
...this.userDisplayProperties,
|
||||
...properties,
|
||||
};
|
||||
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.userDisplayProperties = newProperties;
|
||||
});
|
||||
|
||||
await this.issueService.updateIssueDisplayProperties(workspaceSlug, projectId, newProperties);
|
||||
} catch (error) {
|
||||
this.fetchUserProjectFilters(workspaceSlug, projectId);
|
||||
|
||||
runInAction(() => {
|
||||
this.error = error;
|
||||
});
|
||||
|
||||
console.log("Failed to update user display properties in issue filter store", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserProjectFilters = async (workspaceSlug: string, projectId: string) => {
|
||||
try {
|
||||
const memberResponse = await this.projectService.projectMemberMe(workspaceSlug, projectId);
|
||||
const issueProperties = await this.issueService.getIssueDisplayProperties(workspaceSlug, projectId);
|
||||
|
||||
runInAction(() => {
|
||||
this.userFilters = memberResponse?.view_props?.filters;
|
||||
this.userDisplayFilters = {
|
||||
...(memberResponse?.view_props?.display_filters ?? {}),
|
||||
// set layout to list if layout is not list or kanban
|
||||
layout:
|
||||
memberResponse?.view_props?.display_filters?.layout === "list" ||
|
||||
memberResponse?.view_props?.display_filters?.layout === "kanban"
|
||||
? memberResponse?.view_props?.display_filters?.layout
|
||||
: "list",
|
||||
};
|
||||
this.userDisplayProperties = issueProperties?.properties;
|
||||
});
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
this.error = error;
|
||||
});
|
||||
|
||||
console.log("Failed to fetch user filters in issue filter store", error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user