mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: store updates
This commit is contained in:
parent
984f7ed6b8
commit
d57d91e530
59
packages/types/src/view/base.d.ts
vendored
59
packages/types/src/view/base.d.ts
vendored
@ -1,34 +1,45 @@
|
||||
import { TFilters, TDisplayFilters, TDisplayProperties } from "./filter";
|
||||
import {
|
||||
TViewFilters,
|
||||
TViewDisplayFilters,
|
||||
TViewDisplayProperties,
|
||||
} from "./filter";
|
||||
|
||||
declare enum EGlobalViewAccess {
|
||||
export type TViewTypes =
|
||||
| "WORKSPACE_YOUR_VIEWS"
|
||||
| "WORKSPACE_VIEWS"
|
||||
| "WORKSPACE_PROJECT_VIEWS"
|
||||
| "PROJECT_VIEWS"
|
||||
| "PROJECT_YOUR_VIEWS";
|
||||
|
||||
declare enum EViewAccess {
|
||||
"public" = 0,
|
||||
"private" = 1,
|
||||
"shared" = 2,
|
||||
}
|
||||
|
||||
export type TViewAccess =
|
||||
| EGlobalViewAccess.public
|
||||
| EGlobalViewAccess.private
|
||||
| EGlobalViewAccess.shared;
|
||||
| EViewAccess.public
|
||||
| EViewAccess.private
|
||||
| EViewAccess.shared;
|
||||
|
||||
export type TView = {
|
||||
readonly id: string;
|
||||
readonly workspace: string;
|
||||
readonly project: string | undefined;
|
||||
name: string;
|
||||
description: string;
|
||||
readonly query: string;
|
||||
filters: TFilters;
|
||||
display_filters: TDisplayFilters;
|
||||
display_properties: TDisplayProperties;
|
||||
readonly access: TViewAccess;
|
||||
readonly owned_by: string;
|
||||
readonly sort_order: number;
|
||||
readonly is_locked: boolean;
|
||||
readonly is_pinned: boolean;
|
||||
readonly is_favorite: boolean;
|
||||
readonly created_by: string;
|
||||
readonly updated_by: string;
|
||||
readonly created_at: Date;
|
||||
readonly updated_at: Date;
|
||||
id: string | undefined;
|
||||
workspace: string | undefined;
|
||||
project: string | undefined;
|
||||
name: string | undefined;
|
||||
description: string | undefined;
|
||||
query: string | undefined;
|
||||
filters: TViewFilters | undefined;
|
||||
display_filters: TViewDisplayFilters | undefined;
|
||||
display_properties: TViewDisplayProperties | undefined;
|
||||
access: TViewAccess | undefined;
|
||||
owned_by: string | undefined;
|
||||
sort_order: number | undefined;
|
||||
is_locked: boolean | undefined;
|
||||
is_pinned: boolean | undefined;
|
||||
is_favorite: boolean | undefined;
|
||||
created_by: string | undefined;
|
||||
updated_by: string | undefined;
|
||||
created_at: Date | undefined;
|
||||
updated_at: Date | undefined;
|
||||
};
|
||||
|
37
packages/types/src/view/filter.d.ts
vendored
37
packages/types/src/view/filter.d.ts
vendored
@ -1,8 +1,13 @@
|
||||
export type TLayouts = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt";
|
||||
export type TViewLayouts =
|
||||
| "list"
|
||||
| "kanban"
|
||||
| "calendar"
|
||||
| "spreadsheet"
|
||||
| "gantt";
|
||||
|
||||
export type TCalendarLayouts = "month" | "week";
|
||||
export type TViewCalendarLayouts = "month" | "week";
|
||||
|
||||
export type TFilters = {
|
||||
export type TViewFilters = {
|
||||
project: string[];
|
||||
priority: string[];
|
||||
state: string[];
|
||||
@ -16,8 +21,8 @@ export type TFilters = {
|
||||
target_date: string[];
|
||||
};
|
||||
|
||||
export type TDisplayFilters = {
|
||||
layout: TLayouts;
|
||||
export type TViewDisplayFilters = {
|
||||
layout: TViewLayouts;
|
||||
group_by: string | undefined;
|
||||
sub_group_by: string | undefined;
|
||||
order_by: string;
|
||||
@ -26,11 +31,11 @@ export type TDisplayFilters = {
|
||||
show_empty_groups: boolean;
|
||||
calendar: {
|
||||
show_weekends: boolean;
|
||||
layout: TCalendarLayouts;
|
||||
layout: TViewCalendarLayouts;
|
||||
};
|
||||
};
|
||||
|
||||
export type TDisplayProperties = {
|
||||
export type TViewDisplayProperties = {
|
||||
assignee: boolean;
|
||||
start_date: boolean;
|
||||
due_date: boolean;
|
||||
@ -46,19 +51,19 @@ export type TDisplayProperties = {
|
||||
updated_on: boolean;
|
||||
};
|
||||
|
||||
export type TFilterProps = {
|
||||
filters: TFilters;
|
||||
display_filters: TDisplayFilters;
|
||||
display_properties: TDisplayProperties;
|
||||
export type TViewFilterProps = {
|
||||
filters: TViewFilters | undefined;
|
||||
display_filters: TViewDisplayFilters | undefined;
|
||||
display_properties: TViewDisplayProperties | undefined;
|
||||
};
|
||||
|
||||
export type TFilterPartialProps = {
|
||||
filters: Partial<TFilters>;
|
||||
display_filters: Partial<TDisplayFilters>;
|
||||
display_properties: Partial<TDisplayProperties>;
|
||||
export type TViewFilterPartialProps = {
|
||||
filters: Partial<TViewFilters> | undefined;
|
||||
display_filters: Partial<TViewDisplayFilters> | undefined;
|
||||
display_properties: Partial<TViewDisplayProperties> | undefined;
|
||||
};
|
||||
|
||||
export type TFilterQueryParams =
|
||||
export type TViewFilterQueryParams =
|
||||
| "project"
|
||||
| "priority"
|
||||
| "state"
|
||||
|
1
packages/types/src/view/root.d.ts
vendored
1
packages/types/src/view/root.d.ts
vendored
@ -1,2 +1,3 @@
|
||||
export * from "./filter";
|
||||
export * from "./base";
|
||||
export * from "./user-base";
|
||||
|
21
packages/types/src/view/user-base.d.ts
vendored
Normal file
21
packages/types/src/view/user-base.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import {
|
||||
TViewFilters,
|
||||
TViewDisplayFilters,
|
||||
TViewDisplayProperties,
|
||||
} from "./filter";
|
||||
|
||||
export type TUserView = {
|
||||
id: string | undefined;
|
||||
workspace: string | undefined;
|
||||
project: string | undefined;
|
||||
module: string | undefined;
|
||||
cycle: string | undefined;
|
||||
filters: TViewFilters | undefined;
|
||||
display_filters: TViewDisplayFilters | undefined;
|
||||
display_properties: TViewDisplayProperties | undefined;
|
||||
user: string | undefined;
|
||||
created_by: string | undefined;
|
||||
updated_by: string | undefined;
|
||||
created_at: Date | undefined;
|
||||
updated_at: Date | undefined;
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { FC, } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
// components
|
||||
import { IssuePeekOverview } from "components/issues";
|
||||
|
17
web/components/view/applied-filters/filter-item.tsx
Normal file
17
web/components/view/applied-filters/filter-item.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { FC } from "react";
|
||||
|
||||
type TViewFiltersItem = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
};
|
||||
|
||||
export const ViewFiltersItem: FC<TViewFiltersItem> = (props) => {
|
||||
const { workspaceSlug, projectId, viewId } = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>ViewFiltersItem</div>
|
||||
</div>
|
||||
);
|
||||
};
|
51
web/components/view/applied-filters/filter.tsx
Normal file
51
web/components/view/applied-filters/filter.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { X } from "lucide-react";
|
||||
// hooks
|
||||
import { useViewDetail } from "hooks/store";
|
||||
// helpers
|
||||
import { generateTitle } from "./helper";
|
||||
// types
|
||||
import { TFilters } from "@plane/types";
|
||||
|
||||
type TViewAppliedFilters = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string;
|
||||
filterKey: keyof TFilters;
|
||||
};
|
||||
|
||||
export const ViewAppliedFilters: FC<TViewAppliedFilters> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId, filterKey } = props;
|
||||
|
||||
const view = useViewDetail("WORKSPACE", workspaceSlug, projectId, viewId);
|
||||
|
||||
const filterKeyValue =
|
||||
view?.appliedFilters?.filters && !isEmpty(view?.appliedFilters?.filters)
|
||||
? view?.appliedFilters?.filters?.[filterKey] || undefined
|
||||
: undefined;
|
||||
|
||||
if (!filterKeyValue || filterKeyValue.length <= 0) return <></>;
|
||||
return (
|
||||
<div key={filterKey} className="relative flex items-center gap-2 border border-custom-border-300 rounded p-1.5">
|
||||
<div className="flex-shrink-0 text-xs">{generateTitle(filterKey)}</div>
|
||||
<div className="border border-red-500 relative flex items-center">
|
||||
{/* <div className="relative flex items-center">
|
||||
<div>Icon</div>
|
||||
<div>Title</div>
|
||||
<div>Close</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>Icon</div>
|
||||
<div>Title</div>
|
||||
<div>Close</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 rounded-full cursor-pointer transition-all bg-custom-background-80 hover:bg-custom-background-90 text-custom-text-300 hover:text-custom-text-200">
|
||||
<X size={10} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
66
web/components/view/applied-filters/helper.tsx
Normal file
66
web/components/view/applied-filters/helper.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { ReactNode } from "react";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
// types
|
||||
import { TFilters } from "@plane/types";
|
||||
|
||||
type TComputedAppliedFilters = {
|
||||
key: string;
|
||||
title: string;
|
||||
selectedOptions?: { id: string; icon: ""; title: ""; component: ReactNode }[];
|
||||
dropdownOptions?: { id: string; icon: ""; title: ""; component: ReactNode }[];
|
||||
}[];
|
||||
|
||||
export const filterOptions = (key: keyof TFilters, selectedFilters: string[]) => {
|
||||
switch (key) {
|
||||
case "project":
|
||||
return [];
|
||||
case "priority":
|
||||
return [];
|
||||
case "state":
|
||||
return [];
|
||||
case "state_group":
|
||||
return [];
|
||||
case "assignees":
|
||||
return [];
|
||||
case "mentions":
|
||||
return [];
|
||||
case "subscriber":
|
||||
return [];
|
||||
case "created_by":
|
||||
return [];
|
||||
case "labels":
|
||||
return [];
|
||||
case "start_date":
|
||||
return [];
|
||||
case "target_date":
|
||||
return [];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const generateTitle = (title: string) =>
|
||||
title
|
||||
.split("_")
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ");
|
||||
|
||||
export const constructAppliedFilters = (filters: TFilters): TComputedAppliedFilters => {
|
||||
const appliedFilters: TComputedAppliedFilters = [];
|
||||
|
||||
if (filters && !isEmpty(filters)) {
|
||||
Object.keys(filters).forEach((_filterKey) => {
|
||||
const _key = _filterKey as keyof TFilters;
|
||||
const _value = filters[_key];
|
||||
|
||||
if (_value && !isEmpty(_value)) {
|
||||
appliedFilters.push({
|
||||
key: _key,
|
||||
title: generateTitle(_key),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return appliedFilters;
|
||||
};
|
45
web/components/view/applied-filters/root.tsx
Normal file
45
web/components/view/applied-filters/root.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
// hooks
|
||||
import { useViewDetail } from "hooks/store";
|
||||
// components
|
||||
import { ViewAppliedFilters } from "./filter";
|
||||
// types
|
||||
import { TFilters } from "@plane/types";
|
||||
import { TViewOperations } from "../types";
|
||||
|
||||
type TViewAppliedFiltersRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string;
|
||||
viewOperations: TViewOperations;
|
||||
};
|
||||
|
||||
export const ViewAppliedFiltersRoot: FC<TViewAppliedFiltersRoot> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId } = props;
|
||||
// hooks
|
||||
const view = useViewDetail("WORKSPACE", workspaceSlug, projectId, viewId);
|
||||
|
||||
const filterKeys =
|
||||
view?.appliedFilters && !isEmpty(view?.appliedFilters?.filters)
|
||||
? Object.keys(view?.appliedFilters?.filters)
|
||||
: undefined;
|
||||
|
||||
if (!filterKeys) return <></>;
|
||||
return (
|
||||
<div className="relative flex items-center gap-2 flex-wrap border border-red-500 p-4">
|
||||
{filterKeys.map((key) => {
|
||||
const filterKey = key as keyof TFilters;
|
||||
return (
|
||||
<ViewAppliedFilters
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={viewId}
|
||||
filterKey={filterKey}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
});
|
17
web/components/view/display-filters/root.tsx
Normal file
17
web/components/view/display-filters/root.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { FC } from "react";
|
||||
|
||||
type TViewDisplayFiltersRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
};
|
||||
|
||||
export const ViewDisplayFiltersRoot: FC<TViewDisplayFiltersRoot> = (props) => {
|
||||
const { workspaceSlug, projectId, viewId } = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>ViewDisplayFiltersRoot</div>
|
||||
</div>
|
||||
);
|
||||
};
|
17
web/components/view/display-properties/root.tsx
Normal file
17
web/components/view/display-properties/root.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { FC } from "react";
|
||||
|
||||
type TViewDisplayPropertiesRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
};
|
||||
|
||||
export const ViewDisplayPropertiesRoot: FC<TViewDisplayPropertiesRoot> = (props) => {
|
||||
const { workspaceSlug, projectId, viewId } = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>ViewDisplayPropertiesRoot</div>
|
||||
</div>
|
||||
);
|
||||
};
|
34
web/components/view/filters/root.tsx
Normal file
34
web/components/view/filters/root.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { FC } from "react";
|
||||
// types
|
||||
import { TViewOperations } from "../types";
|
||||
|
||||
type TViewFiltersRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
viewOperations: TViewOperations;
|
||||
};
|
||||
|
||||
export const ViewFiltersRoot: FC<TViewFiltersRoot> = (props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewOperations } = props;
|
||||
|
||||
const filters = {
|
||||
project: ["1", "2", "3", "4", "5", "6"],
|
||||
priority: ["1", "2", "3", "4", "5", "6"],
|
||||
state: ["1", "2", "3", "4", "5", "6"],
|
||||
state_group: ["1", "2", "3", "4", "5", "6"],
|
||||
assignees: ["1", "2", "3", "4", "5", "6"],
|
||||
mentions: ["1", "2", "3", "4", "5", "6"],
|
||||
subscriber: ["1", "2", "3", "4", "5", "6"],
|
||||
created_by: ["1", "2", "3", "4", "5", "6"],
|
||||
labels: ["1", "2", "3", "4", "5", "6"],
|
||||
start_date: ["1", "2", "3", "4", "5", "6"],
|
||||
target_date: ["1", "2", "3", "4", "5", "6"],
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border border-red-500">
|
||||
<div>ViewFiltersRoot</div>
|
||||
</div>
|
||||
);
|
||||
};
|
18
web/components/view/index.ts
Normal file
18
web/components/view/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export * from "./root";
|
||||
|
||||
// views
|
||||
export * from "./views/root";
|
||||
export * from "./views/create-edit";
|
||||
export * from "./views/create-edit-form";
|
||||
|
||||
// view filters
|
||||
export * from "./filters/root";
|
||||
|
||||
// view display filters
|
||||
export * from "./display-filters/root";
|
||||
|
||||
// view display properties
|
||||
export * from "./display-properties/root";
|
||||
|
||||
// view applied filters
|
||||
export * from "./applied-filters/root";
|
77
web/components/view/root.tsx
Normal file
77
web/components/view/root.tsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { FC, ReactNode, useEffect, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useView } from "hooks/store/use-view";
|
||||
// components
|
||||
import { ViewRoot, ViewCreateEdit, ViewFiltersRoot, ViewAppliedFiltersRoot } from "./";
|
||||
// types
|
||||
import { TViewOperations } from "./types";
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
type TWorkspaceViewRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
viewType: TViewTypes;
|
||||
};
|
||||
|
||||
export const WorkspaceViewRoot: FC<TWorkspaceViewRoot> = observer((props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewType } = props;
|
||||
// hooks
|
||||
const views = useView(workspaceSlug, projectId, viewType);
|
||||
|
||||
const viewOperations: TViewOperations = useMemo(
|
||||
() => ({
|
||||
create: async (data) => {
|
||||
await views?.create(data);
|
||||
},
|
||||
fetch: async () => {
|
||||
await views?.fetch();
|
||||
},
|
||||
}),
|
||||
[views]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (workspaceSlug && viewId && viewOperations) viewOperations.fetch();
|
||||
}, [workspaceSlug, viewId, viewOperations]);
|
||||
|
||||
console.log("views?.viewMap", Object.keys(views?.viewMap).length);
|
||||
|
||||
Object.keys(views?.viewMap).map((viewId) => {
|
||||
console.log(views?.viewMap?.[viewId]?.access);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full border border-red-500">
|
||||
<ViewCreateEdit
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={viewId}
|
||||
viewType={viewType}
|
||||
viewOperations={viewOperations}
|
||||
/>
|
||||
|
||||
{/* <ViewRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={viewId}
|
||||
viewType={viewType}
|
||||
viewOperations={viewOperations}
|
||||
/> */}
|
||||
|
||||
{/* <ViewFiltersRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={"61d6b507-ae5c-45d6-b169-da7162f016a0"}
|
||||
viewOperations={viewOperations}
|
||||
/>
|
||||
<ViewAppliedFiltersRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
viewId={"61d6b507-ae5c-45d6-b169-da7162f016a0"}
|
||||
viewOperations={viewOperations}
|
||||
/> */}
|
||||
</div>
|
||||
);
|
||||
});
|
6
web/components/view/types.d.ts
vendored
Normal file
6
web/components/view/types.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import { TView } from "@plane/types";
|
||||
|
||||
export type TViewOperations = {
|
||||
create: (data: Partial<TView>) => void;
|
||||
fetch: () => void;
|
||||
};
|
104
web/components/view/views/create-edit-form.tsx
Normal file
104
web/components/view/views/create-edit-form.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { FC, Fragment } from "react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Trash2, Plus, X } from "lucide-react";
|
||||
// ui
|
||||
import { Input, Button } from "@plane/ui";
|
||||
// types
|
||||
import { TViewOperations } from "../types";
|
||||
|
||||
type TViewCreateEditForm = {
|
||||
modalToggle: boolean;
|
||||
|
||||
handleModalClose: () => void;
|
||||
viewOperations?: TViewOperations;
|
||||
};
|
||||
|
||||
export const ViewCreateEditForm: FC<TViewCreateEditForm> = (props) => {
|
||||
const { modalToggle, handleModalClose, viewOperations } = props;
|
||||
|
||||
const createView = () => {
|
||||
viewOperations?.create({ name: "create" });
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition.Root show={modalToggle} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={handleModalClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-custom-backdrop transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[40rem] py-5 border-[0.1px] border-custom-border-100">
|
||||
<div className="p-3 px-5 relative flex items-center gap-2">
|
||||
<div className="relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80">
|
||||
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden">
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</div>
|
||||
<div className="text-xs uppercase">Project Identifier</div>
|
||||
</div>
|
||||
<div className="">Create|Edit View</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 px-5">
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
// value={value}
|
||||
// onChange={onChange}
|
||||
// hasError={Boolean(errors.email)}
|
||||
placeholder="What do you want to call this view?"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 pr-12 placeholder:text-onboarding-text-400"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-3 px-5 relative flex justify-between items-center gap-2">
|
||||
<div className="relative rounded p-1.5 px-2 flex items-center gap-1 border border-custom-border-100 bg-custom-background-80">
|
||||
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden">
|
||||
<Plus className="w-3 h-3" />
|
||||
</div>
|
||||
<div className="text-xs">Filters</div>
|
||||
</div>
|
||||
<div className="relative rounded p-1.5 px-2 flex items-center gap-1 border border-dashed border-custom-border-100 bg-custom-background-80">
|
||||
<div className="text-xs">Clear all filters</div>
|
||||
<div className="flex-shrink-0 relative flex justify-center items-center w-4 h-4 overflow-hidden">
|
||||
<X className="w-3 h-3" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-3 px-5 relative bg-custom-background-80">Applied Filters with each dropdown</div>
|
||||
|
||||
<div className="p-3 px-5 relative flex justify-end items-center gap-2">
|
||||
<Button variant="neutral-primary" onClick={handleModalClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary">Create View</Button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
42
web/components/view/views/create-edit.tsx
Normal file
42
web/components/view/views/create-edit.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { FC, useState } from "react";
|
||||
import { Plus } from "lucide-react";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// components
|
||||
import { ViewCreateEditForm } from "./create-edit-form";
|
||||
// types
|
||||
import { TViewOperations } from "../types";
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
type TViewCreateEdit = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
viewType: TViewTypes;
|
||||
viewOperations: TViewOperations;
|
||||
};
|
||||
|
||||
export const ViewCreateEdit: FC<TViewCreateEdit> = (props) => {
|
||||
const { workspaceSlug, projectId, viewId, viewOperations } = props;
|
||||
// states
|
||||
const [modalToggle, setModalToggle] = useState(false);
|
||||
|
||||
const handleModalOpen = () => setModalToggle(true);
|
||||
const handleModalClose = () => setModalToggle(false);
|
||||
|
||||
const createView = () => {
|
||||
viewOperations?.create({ name: "create" });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewCreateEditForm modalToggle={modalToggle} handleModalClose={handleModalClose} />
|
||||
<div className="border border-red-500 p-5">
|
||||
<Button size="sm" className="flex justify-center items-center" onClick={createView}>
|
||||
<Plus size={12} />
|
||||
<span>New View</span>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
36
web/components/view/views/root.tsx
Normal file
36
web/components/view/views/root.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { FC } from "react";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
// types
|
||||
import { TViewOperations } from "../types";
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
type TViewRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string | undefined;
|
||||
viewId: string | undefined;
|
||||
viewType: TViewTypes;
|
||||
viewOperations: TViewOperations;
|
||||
};
|
||||
|
||||
export const ViewRoot: FC<TViewRoot> = (props) => {
|
||||
const {} = props;
|
||||
|
||||
return (
|
||||
<div className="border border-red-500 relative flex items-center gap-2">
|
||||
{/* header */}
|
||||
<div className="">Workspace Views</div>
|
||||
{/* divider */}
|
||||
<div className="relative w-[50px] h-[50px] flex justify-center items-center">
|
||||
<div className="absolute top-0 bottom-0 border border-red-500 w-[0.5px]" />
|
||||
<div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center rounded-full bg-red-500">
|
||||
<ChevronRight size={12} />
|
||||
</div>
|
||||
</div>
|
||||
{/* views content */}
|
||||
<div className=" relative flex items-center">
|
||||
<div>Icon</div>
|
||||
<div>Title</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
47
web/constants/view.ts
Normal file
47
web/constants/view.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
export const VIEW_TYPES: Record<TViewTypes, TViewTypes> = {
|
||||
WORKSPACE_YOUR_VIEWS: "WORKSPACE_YOUR_VIEWS",
|
||||
WORKSPACE_VIEWS: "WORKSPACE_VIEWS",
|
||||
WORKSPACE_PROJECT_VIEWS: "WORKSPACE_PROJECT_VIEWS",
|
||||
PROJECT_VIEWS: "PROJECT_VIEWS",
|
||||
PROJECT_YOUR_VIEWS: "PROJECT_YOUR_VIEWS",
|
||||
};
|
||||
|
||||
export const VIEW_DEFAULT_FILTER_PARAMETERS = {
|
||||
filters: {
|
||||
default: [
|
||||
"project",
|
||||
"priority",
|
||||
"state",
|
||||
"state_group",
|
||||
"assignees",
|
||||
"mentions",
|
||||
"subscriber",
|
||||
"created_by",
|
||||
"labels",
|
||||
"start_date",
|
||||
"target_date",
|
||||
],
|
||||
},
|
||||
display_filters: {
|
||||
default: ["layout", "group_by", "sub_group_by", "order_by", "type", "sub_issue", "show_empty_groups", "calendar"],
|
||||
},
|
||||
display_properties: {
|
||||
default: [
|
||||
"assignee",
|
||||
"start_date",
|
||||
"due_date",
|
||||
"labels",
|
||||
"key",
|
||||
"priority",
|
||||
"state",
|
||||
"sub_issue_count",
|
||||
"link",
|
||||
"attachment_count",
|
||||
"estimate",
|
||||
"created_on",
|
||||
"updated_on",
|
||||
],
|
||||
},
|
||||
};
|
@ -21,3 +21,7 @@ export * from "./use-kanban-view";
|
||||
export * from "./use-issue-detail";
|
||||
export * from "./use-inbox";
|
||||
export * from "./use-inbox-issues";
|
||||
|
||||
// new store
|
||||
export * from "./use-view";
|
||||
export * from "./use-view-detail";
|
||||
|
38
web/hooks/store/use-view-detail.tsx
Normal file
38
web/hooks/store/use-view-detail.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { useContext } from "react";
|
||||
// mobx store
|
||||
import { StoreContext } from "contexts/store-context";
|
||||
// store
|
||||
import { TViewStore } from "store/view/view.store";
|
||||
// types
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
export const useViewDetail = (
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
viewId: string,
|
||||
viewType: TViewTypes | undefined
|
||||
): TViewStore | undefined => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useViewDetail must be used within StoreProvider");
|
||||
|
||||
if (!workspaceSlug) throw new Error("useViewDetail hook must require workspaceSlug");
|
||||
|
||||
if (!viewId) throw new Error("useViewDetail hook must require viewId");
|
||||
|
||||
switch (viewType) {
|
||||
case "WORKSPACE_YOUR_VIEWS":
|
||||
return context.view.workspaceViewStore.viewById(viewId);
|
||||
case "WORKSPACE_VIEWS":
|
||||
return context.view.workspaceViewMeStore.viewById(viewId);
|
||||
case "WORKSPACE_PROJECT_VIEWS":
|
||||
return context.view.workspaceViewMeStore.viewById(viewId);
|
||||
case "PROJECT_YOUR_VIEWS":
|
||||
if (!projectId) throw new Error("useView hook must require projectId");
|
||||
return context.view.projectViewMeStore.viewById(viewId);
|
||||
case "PROJECT_VIEWS":
|
||||
if (!projectId) throw new Error("useView hook must require projectId");
|
||||
return context.view.projectViewStore.viewById(viewId);
|
||||
default:
|
||||
throw new Error("useView hook must require viewType");
|
||||
}
|
||||
};
|
35
web/hooks/store/use-view.tsx
Normal file
35
web/hooks/store/use-view.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { useContext } from "react";
|
||||
// mobx store
|
||||
import { StoreContext } from "contexts/store-context";
|
||||
// types
|
||||
import { ViewRoot } from "store/view/view-root.store";
|
||||
// types
|
||||
import { TViewTypes } from "@plane/types";
|
||||
|
||||
export const useView = (
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
viewType: TViewTypes | undefined
|
||||
): ViewRoot => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useView must be used within StoreProvider");
|
||||
|
||||
if (!workspaceSlug) throw new Error("useView hook must require workspaceSlug");
|
||||
|
||||
switch (viewType) {
|
||||
case "WORKSPACE_YOUR_VIEWS":
|
||||
return context.view.workspaceViewStore;
|
||||
case "WORKSPACE_VIEWS":
|
||||
return context.view.workspaceViewMeStore;
|
||||
case "WORKSPACE_PROJECT_VIEWS":
|
||||
return context.view.workspaceViewMeStore;
|
||||
case "PROJECT_YOUR_VIEWS":
|
||||
if (!projectId) throw new Error("useView hook must require projectId");
|
||||
return context.view.projectViewMeStore;
|
||||
case "PROJECT_VIEWS":
|
||||
if (!projectId) throw new Error("useView hook must require projectId");
|
||||
return context.view.projectViewStore;
|
||||
default:
|
||||
throw new Error("useView hook must require viewType");
|
||||
}
|
||||
};
|
@ -10,23 +10,24 @@ export class IssueFiltersService extends APIService {
|
||||
}
|
||||
|
||||
// // workspace issue filters
|
||||
// async fetchWorkspaceFilters(workspaceSlug: string): Promise<IIssueFiltersResponse> {
|
||||
// return this.get(`/api/workspaces/${workspaceSlug}/user-properties/`)
|
||||
// .then((response) => response?.data)
|
||||
// .catch((error) => {
|
||||
// throw error?.response?.data;
|
||||
// });
|
||||
// }
|
||||
// async patchWorkspaceFilters(
|
||||
// workspaceSlug: string,
|
||||
// data: Partial<IIssueFiltersResponse>
|
||||
// ): Promise<IIssueFiltersResponse> {
|
||||
// return this.patch(`/api/workspaces/${workspaceSlug}/user-properties/`, data)
|
||||
// .then((response) => response?.data)
|
||||
// .catch((error) => {
|
||||
// throw error?.response?.data;
|
||||
// });
|
||||
// }
|
||||
async fetchWorkspaceFilters(workspaceSlug: string): Promise<IIssueFiltersResponse> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/user-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async patchWorkspaceFilters(
|
||||
workspaceSlug: string,
|
||||
data: Partial<IIssueFiltersResponse>
|
||||
): Promise<IIssueFiltersResponse> {
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/user-properties/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
// project issue filters
|
||||
async fetchProjectIssueFilters(workspaceSlug: string, projectId: string): Promise<IIssueFiltersResponse> {
|
||||
|
14
web/services/view/index.ts
Normal file
14
web/services/view/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// view services
|
||||
export * from "./workspace_me.service";
|
||||
export * from "./workspace.service";
|
||||
export * from "./project_me.service";
|
||||
export * from "./project.service";
|
||||
|
||||
// user view services
|
||||
export * from "./user/workspace.service";
|
||||
export * from "./user/project.service";
|
||||
export * from "./user/module.service";
|
||||
export * from "./user/cycle.service";
|
||||
|
||||
// views that are being stored in the local-store
|
||||
// export * from "./user/local_storage.service";
|
@ -91,7 +91,7 @@ export class ProjectViewService extends APIService implements TViewService {
|
||||
projectId: string | undefined = undefined
|
||||
): Promise<TView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unlock/`)
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/lock/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
@ -130,7 +130,7 @@ export class ProjectViewService extends APIService implements TViewService {
|
||||
projectId: string | undefined = undefined
|
||||
): Promise<TView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unfavorite/`)
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/favorite/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
|
@ -91,7 +91,7 @@ export class ProjectViewMeService extends APIService implements TViewService {
|
||||
projectId: string | undefined = undefined
|
||||
): Promise<TView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unlock/`)
|
||||
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/lock/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
@ -130,7 +130,7 @@ export class ProjectViewMeService extends APIService implements TViewService {
|
||||
projectId: string | undefined = undefined
|
||||
): Promise<TView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unfavorite/`)
|
||||
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/favorite/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
|
25
web/services/view/types.d.ts
vendored
25
web/services/view/types.d.ts
vendored
@ -1,4 +1,15 @@
|
||||
import { TView } from "@plane/types";
|
||||
import { TView, TUserView } from "@plane/types";
|
||||
|
||||
export type TUserViewService = {
|
||||
// featureId represents moduleId/cycleId
|
||||
fetch: (workspaceSlug: string, projectId?: string, featureId?: string) => Promise<TUserView | undefined>;
|
||||
update: (
|
||||
workspaceSlug: string,
|
||||
data: Partial<TView>,
|
||||
projectId?: string,
|
||||
featureId?: string
|
||||
) => Promise<TUserView | undefined>;
|
||||
};
|
||||
|
||||
export type TViewService = {
|
||||
fetch: (workspaceSlug: string, projectId?: string) => Promise<TView[] | undefined>;
|
||||
@ -10,10 +21,10 @@ export type TViewService = {
|
||||
data: Partial<TView>,
|
||||
projectId?: string
|
||||
) => Promise<TView | undefined>;
|
||||
remove: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<void> | undefined;
|
||||
lock: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
unlock: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
duplicate: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
makeFavorite: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
removeFavorite: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
remove?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<void> | undefined;
|
||||
lock?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
unlock?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
duplicate?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
makeFavorite?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
removeFavorite?: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
|
||||
};
|
||||
|
36
web/services/view/user/cycle.service.ts
Normal file
36
web/services/view/user/cycle.service.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// services
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { TViewFilterProps, TUserView } from "@plane/types";
|
||||
import { TUserViewService } from "../types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
|
||||
export class CycleFiltersService extends APIService implements TUserViewService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async fetch(workspaceSlug: string, projectId?: string, cycleId?: string): Promise<TUserView | undefined> {
|
||||
if (!projectId || !cycleId) return undefined;
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}user-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
workspaceSlug: string,
|
||||
data: Partial<TViewFilterProps>,
|
||||
projectId?: string,
|
||||
cycleId?: string
|
||||
): Promise<TUserView | undefined> {
|
||||
if (!projectId || !cycleId) return undefined;
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}user-properties/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
39
web/services/view/user/module.service.ts
Normal file
39
web/services/view/user/module.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// services
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { TViewFilterProps, TUserView } from "@plane/types";
|
||||
import { TUserViewService } from "../types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
|
||||
export class ModuleFiltersService extends APIService implements TUserViewService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async fetch(workspaceSlug: string, projectId?: string, moduleId?: string): Promise<TUserView | undefined> {
|
||||
if (!projectId || !moduleId) return undefined;
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}user-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
workspaceSlug: string,
|
||||
data: Partial<TViewFilterProps>,
|
||||
projectId?: string,
|
||||
moduleId?: string
|
||||
): Promise<TUserView | undefined> {
|
||||
if (!projectId || !moduleId) return undefined;
|
||||
return this.patch(
|
||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}user-properties/`,
|
||||
data
|
||||
)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
35
web/services/view/user/project.service.ts
Normal file
35
web/services/view/user/project.service.ts
Normal file
@ -0,0 +1,35 @@
|
||||
// services
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { TViewFilterProps, TUserView } from "@plane/types";
|
||||
import { TUserViewService } from "../types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
|
||||
export class ProjectFiltersService extends APIService implements TUserViewService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async fetch(workspaceSlug: string, projectId?: string): Promise<TUserView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/user-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async update(
|
||||
workspaceSlug: string,
|
||||
data: Partial<TViewFilterProps>,
|
||||
projectId?: string
|
||||
): Promise<TUserView | undefined> {
|
||||
if (!projectId) return undefined;
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/user-properties/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
29
web/services/view/user/workspace.service.ts
Normal file
29
web/services/view/user/workspace.service.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// services
|
||||
import { APIService } from "services/api.service";
|
||||
// types
|
||||
import type { TViewFilterProps, TUserView } from "@plane/types";
|
||||
import { TUserViewService } from "../types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "helpers/common.helper";
|
||||
|
||||
export class WorkspaceFiltersService extends APIService implements TUserViewService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async fetch(workspaceSlug: string): Promise<TUserView | undefined> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/user-properties/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async update(workspaceSlug: string, data: Partial<TViewFilterProps>): Promise<TUserView | undefined> {
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/user-properties/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ export class WorkspaceViewService extends APIService implements TViewService {
|
||||
}
|
||||
|
||||
async unlock(workspaceSlug: string, viewId: string): Promise<TView> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/views/${viewId}/unlock/`)
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/views/${viewId}/lock/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
@ -83,7 +83,7 @@ export class WorkspaceViewService extends APIService implements TViewService {
|
||||
}
|
||||
|
||||
async removeFavorite(workspaceSlug: string, viewId: string): Promise<TView> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/views/${viewId}/unfavorite/`)
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/views/${viewId}/favorite/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
|
@ -11,7 +11,7 @@ export class WorkspaceMeViewService extends APIService implements TViewService {
|
||||
}
|
||||
|
||||
async fetch(workspaceSlug: string): Promise<TView[]> {
|
||||
return this.get(`/api/users/me/workspaces/${workspaceSlug}/views/`)
|
||||
return this.get(`/api/users/me/workspaces/${workspaceSlug}/views/?type=workspace`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
@ -59,7 +59,7 @@ export class WorkspaceMeViewService extends APIService implements TViewService {
|
||||
}
|
||||
|
||||
async unlock(workspaceSlug: string, viewId: string): Promise<TView> {
|
||||
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/unlock/`)
|
||||
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/lock/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
@ -83,7 +83,7 @@ export class WorkspaceMeViewService extends APIService implements TViewService {
|
||||
}
|
||||
|
||||
async removeFavorite(workspaceSlug: string, viewId: string): Promise<TView> {
|
||||
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/unfavorite/`)
|
||||
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/favorite/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
|
@ -17,10 +17,14 @@ import { IMentionStore, MentionStore } from "./mention.store";
|
||||
import { DashboardStore, IDashboardStore } from "./dashboard.store";
|
||||
import { IProjectPageStore, ProjectPageStore } from "./project-page.store";
|
||||
import { ILabelStore, LabelStore } from "./label.store";
|
||||
// new stores
|
||||
import { GlobalViewRootStore } from "./view/root.store";
|
||||
|
||||
enableStaticRendering(typeof window === "undefined");
|
||||
|
||||
export class RootStore {
|
||||
view: GlobalViewRootStore;
|
||||
// old store structure
|
||||
app: IAppRootStore;
|
||||
user: IUserRootStore;
|
||||
workspaceRoot: IWorkspaceRootStore;
|
||||
@ -40,6 +44,8 @@ export class RootStore {
|
||||
projectPages: IProjectPageStore;
|
||||
|
||||
constructor() {
|
||||
this.view = new GlobalViewRootStore(this);
|
||||
// old store structure
|
||||
this.app = new AppRootStore(this);
|
||||
this.user = new UserRootStore(this);
|
||||
this.workspaceRoot = new WorkspaceRootStore(this);
|
||||
|
@ -1,10 +1,16 @@
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
// types
|
||||
import { TFilters, TDisplayFilters, TDisplayProperties, TFilterProps, TFilterQueryParams } from "@plane/types";
|
||||
import {
|
||||
TViewFilters,
|
||||
TViewDisplayFilters,
|
||||
TViewDisplayProperties,
|
||||
TViewFilterProps,
|
||||
TViewFilterQueryParams,
|
||||
} from "@plane/types";
|
||||
|
||||
export class FiltersHelper {
|
||||
// computed filters
|
||||
computedFilters = (filters: TFilters, defaultValues?: Partial<TFilters>): TFilters => ({
|
||||
computedFilters = (filters: TViewFilters, defaultValues?: Partial<TViewFilters>): TViewFilters => ({
|
||||
project: filters?.project || defaultValues?.project || [],
|
||||
priority: filters?.priority || defaultValues?.priority || [],
|
||||
state: filters?.state || defaultValues?.state || [],
|
||||
@ -20,9 +26,9 @@ export class FiltersHelper {
|
||||
|
||||
// computed display filters
|
||||
computedDisplayFilters = (
|
||||
displayFilters: TDisplayFilters,
|
||||
defaultValues?: Partial<TDisplayFilters>
|
||||
): TDisplayFilters => ({
|
||||
displayFilters: TViewDisplayFilters,
|
||||
defaultValues?: Partial<TViewDisplayFilters>
|
||||
): TViewDisplayFilters => ({
|
||||
layout: displayFilters?.layout || defaultValues?.layout || "list",
|
||||
group_by: displayFilters?.group_by || defaultValues?.group_by || "none",
|
||||
sub_group_by: displayFilters?.sub_group_by || defaultValues?.sub_group_by || undefined,
|
||||
@ -38,9 +44,9 @@ export class FiltersHelper {
|
||||
|
||||
// computed display properties
|
||||
computedDisplayProperties = (
|
||||
displayProperties: TDisplayProperties,
|
||||
defaultValues?: Partial<TDisplayProperties>
|
||||
): TDisplayProperties => ({
|
||||
displayProperties: TViewDisplayProperties,
|
||||
defaultValues?: Partial<TViewDisplayProperties>
|
||||
): TViewDisplayProperties => ({
|
||||
assignee: displayProperties?.assignee || defaultValues?.assignee || true,
|
||||
start_date: displayProperties?.start_date || defaultValues?.start_date || true,
|
||||
due_date: displayProperties?.due_date || defaultValues?.due_date || true,
|
||||
@ -58,13 +64,13 @@ export class FiltersHelper {
|
||||
|
||||
// compute filters and display_filters issue query parameters
|
||||
computeAppliedFiltersQueryParameters = (
|
||||
filters: TFilterProps,
|
||||
filters: TViewFilterProps,
|
||||
acceptableParamsByLayout: string[]
|
||||
): { params: any; query: string } => {
|
||||
const paramsObject: Partial<Record<TFilterQueryParams, string | boolean>> = {};
|
||||
const paramsObject: Partial<Record<TViewFilterQueryParams, string | boolean>> = {};
|
||||
let paramsString = "";
|
||||
|
||||
const filteredParams: Partial<Record<TFilterQueryParams, undefined | string[] | boolean | string>> = {
|
||||
const filteredParams: Partial<Record<TViewFilterQueryParams, undefined | string[] | boolean | string>> = {
|
||||
// issue filters
|
||||
priority: filters.filters?.priority || undefined,
|
||||
state_group: filters.filters?.state_group || undefined,
|
||||
@ -83,7 +89,7 @@ export class FiltersHelper {
|
||||
};
|
||||
|
||||
Object.keys(filteredParams).forEach((key) => {
|
||||
const _key = key as TFilterQueryParams;
|
||||
const _key = key as TViewFilterQueryParams;
|
||||
const _value: string | boolean | string[] | undefined = filteredParams[_key];
|
||||
if (_value != undefined && acceptableParamsByLayout.includes(_key))
|
||||
paramsObject[_key] = Array.isArray(_value) ? _value.join(",") : _value;
|
||||
@ -92,7 +98,7 @@ export class FiltersHelper {
|
||||
if (paramsObject && !isEmpty(paramsObject)) {
|
||||
paramsString = Object.keys(paramsObject)
|
||||
.map((key) => {
|
||||
const _key = key as TFilterQueryParams;
|
||||
const _key = key as TViewFilterQueryParams;
|
||||
const _value: string | boolean | undefined = paramsObject[_key];
|
||||
if (!undefined) return `${_key}=${_value}`;
|
||||
})
|
@ -1,23 +1,70 @@
|
||||
// services
|
||||
import { WorkspaceViewService } from "services/view/workspace.service";
|
||||
import { WorkspaceMeViewService } from "services/view/workspace_me.service";
|
||||
import { ProjectViewService } from "services/view/project.service";
|
||||
import { ProjectViewMeService } from "services/view/project_me.service";
|
||||
|
||||
import {
|
||||
WorkspaceViewService,
|
||||
WorkspaceMeViewService,
|
||||
ProjectViewService,
|
||||
ProjectViewMeService,
|
||||
WorkspaceFiltersService,
|
||||
ProjectFiltersService,
|
||||
ModuleFiltersService,
|
||||
CycleFiltersService,
|
||||
// LocalStorageFiltersService,
|
||||
} from "services/view";
|
||||
// stores
|
||||
import { ViewRoot } from "./view-root.store";
|
||||
import { ViewRootStore } from "./view-root.store";
|
||||
import { userViewRootStore } from "./user/view-root.store";
|
||||
// types
|
||||
import { RootStore } from "store/root.store";
|
||||
|
||||
export class ViewRootStore {
|
||||
workspaceViewStore: ViewRoot;
|
||||
workspaceViewMeStore: ViewRoot;
|
||||
projectViewStore: ViewRoot;
|
||||
projectViewMeStore: ViewRoot;
|
||||
export class GlobalViewRootStore {
|
||||
// views root
|
||||
workspaceViewMeStore: ViewRootStore;
|
||||
workspaceViewStore: ViewRootStore;
|
||||
workspaceViewProjectStore: ViewRootStore;
|
||||
projectViewStore: ViewRootStore;
|
||||
projectViewMeStore: ViewRootStore;
|
||||
|
||||
// user views root
|
||||
workspaceUserViewStore?: userViewRootStore;
|
||||
projectUserViewStore?: userViewRootStore;
|
||||
moduleUserViewStore?: userViewRootStore;
|
||||
cycleUserViewStore?: userViewRootStore;
|
||||
|
||||
constructor(private store: RootStore) {
|
||||
this.workspaceViewStore = new ViewRoot(this.store, new WorkspaceViewService());
|
||||
this.workspaceViewMeStore = new ViewRoot(this.store, new WorkspaceMeViewService());
|
||||
this.projectViewStore = new ViewRoot(this.store, new ProjectViewService());
|
||||
this.projectViewMeStore = new ViewRoot(this.store, new ProjectViewMeService());
|
||||
// views root
|
||||
this.workspaceViewMeStore = new ViewRootStore(this.store, new WorkspaceMeViewService());
|
||||
this.workspaceViewStore = new ViewRootStore(this.store, new WorkspaceViewService());
|
||||
this.workspaceViewProjectStore = new ViewRootStore(this.store, new WorkspaceMeViewService());
|
||||
this.projectViewStore = new ViewRootStore(this.store, new ProjectViewService());
|
||||
this.projectViewMeStore = new ViewRootStore(this.store, new ProjectViewMeService());
|
||||
|
||||
// user views root
|
||||
this.workspaceUserViewStore = new userViewRootStore(
|
||||
new WorkspaceFiltersService(),
|
||||
store.app?.router?.workspaceSlug,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
this.projectUserViewStore = new userViewRootStore(
|
||||
new ProjectFiltersService(),
|
||||
store.app?.router?.workspaceSlug,
|
||||
store.app?.router?.projectId,
|
||||
undefined
|
||||
);
|
||||
this.moduleUserViewStore = new userViewRootStore(
|
||||
new ModuleFiltersService(),
|
||||
store.app?.router?.workspaceSlug,
|
||||
store.app?.router?.projectId,
|
||||
store.app?.router?.moduleId
|
||||
);
|
||||
this.cycleUserViewStore = new userViewRootStore(
|
||||
new CycleFiltersService(),
|
||||
store.app?.router?.workspaceSlug,
|
||||
store.app?.router?.projectId,
|
||||
store.app?.router?.cycleId
|
||||
);
|
||||
// this.archivedUserViewStore = new userViewRootStore( new LocalStorageFiltersService());
|
||||
// this.draftUserViewStore = new userViewRootStore( new LocalStorageFiltersService());
|
||||
}
|
||||
}
|
||||
|
64
web/store/view/user/view-root.store.ts
Normal file
64
web/store/view/user/view-root.store.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import set from "lodash/set";
|
||||
// stores
|
||||
import { RootStore } from "store/root.store";
|
||||
import { UserViewStore } from "./view.store";
|
||||
// types
|
||||
import { TUserViewService } from "services/view/types";
|
||||
|
||||
type TUserViewRootStore = {
|
||||
// observables
|
||||
viewMap: Record<string, UserViewStore>;
|
||||
// computed
|
||||
viewIds: string[];
|
||||
// helper actions
|
||||
viewById: (viewId: string) => UserViewStore | undefined;
|
||||
// actions
|
||||
fetch: () => Promise<void>;
|
||||
};
|
||||
|
||||
export class userViewRootStore implements TUserViewRootStore {
|
||||
// observables
|
||||
viewMap: Record<string, UserViewStore> = {};
|
||||
|
||||
constructor(
|
||||
private service: TUserViewService,
|
||||
private workspaceSlug: string | undefined,
|
||||
private projectId: string | undefined,
|
||||
private featureId: string | undefined // moduleId/cycleId
|
||||
) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
viewMap: observable.ref,
|
||||
// computed
|
||||
viewIds: computed,
|
||||
// actions
|
||||
fetch: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
get viewIds() {
|
||||
return Object.keys(this.viewMap);
|
||||
}
|
||||
|
||||
// helper actions
|
||||
viewById = (viewId: string) => this.viewMap?.[viewId] || undefined;
|
||||
|
||||
// actions
|
||||
fetch = async () => {
|
||||
if (!this.workspaceSlug) return;
|
||||
|
||||
const view = await this.service.fetch(this.workspaceSlug, this.projectId, this.featureId);
|
||||
if (!view) return;
|
||||
|
||||
runInAction(() => {
|
||||
if (this.workspaceSlug && view.id)
|
||||
set(
|
||||
this.viewMap,
|
||||
[view.id],
|
||||
new UserViewStore(view, this.service, this.workspaceSlug, this.projectId, this.featureId)
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
183
web/store/view/user/view.store.ts
Normal file
183
web/store/view/user/view.store.ts
Normal file
@ -0,0 +1,183 @@
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import set from "lodash/set";
|
||||
// types
|
||||
import { TUserViewService } from "services/view/types";
|
||||
import {
|
||||
TUserView,
|
||||
TViewFilters,
|
||||
TViewDisplayFilters,
|
||||
TViewDisplayProperties,
|
||||
TViewFilterProps,
|
||||
TViewFilterPartialProps,
|
||||
} from "@plane/types";
|
||||
// helpers
|
||||
import { FiltersHelper } from "../helpers/filters_helpers";
|
||||
|
||||
type TLoader = "submitting" | "submit" | undefined;
|
||||
|
||||
export type TUserViewStore = TUserView & {
|
||||
// observables
|
||||
loader: TLoader;
|
||||
filtersToUpdate: TViewFilterPartialProps;
|
||||
// computed
|
||||
appliedFilters: TViewFilterProps | undefined;
|
||||
appliedFiltersQueryParams: string | undefined;
|
||||
// helper actions
|
||||
updateFilters: (filters: Partial<TViewFilters>) => void;
|
||||
updateDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
||||
updateDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
|
||||
resetFilterChanges: () => void;
|
||||
saveFilterChanges: () => void;
|
||||
// actions
|
||||
update: (viewData: Partial<TUserView>) => Promise<void>;
|
||||
};
|
||||
|
||||
export class UserViewStore extends FiltersHelper implements TUserViewStore {
|
||||
id: string | undefined;
|
||||
workspace: string | undefined;
|
||||
project: string | undefined;
|
||||
module: string | undefined;
|
||||
cycle: string | undefined;
|
||||
filters: TViewFilters | undefined;
|
||||
display_filters: TViewDisplayFilters | undefined;
|
||||
display_properties: TViewDisplayProperties | undefined;
|
||||
user: string | undefined;
|
||||
created_by: string | undefined;
|
||||
updated_by: string | undefined;
|
||||
created_at: Date | undefined;
|
||||
updated_at: Date | undefined;
|
||||
|
||||
loader: TLoader = undefined;
|
||||
filtersToUpdate: TViewFilterPartialProps = {
|
||||
filters: {},
|
||||
display_filters: {},
|
||||
display_properties: {},
|
||||
};
|
||||
|
||||
constructor(
|
||||
_view: TUserView,
|
||||
private service: TUserViewService,
|
||||
private workspaceSlug: string,
|
||||
private projectId: string | undefined,
|
||||
private featureId: string | undefined // moduleId/cycleId
|
||||
) {
|
||||
super();
|
||||
this.id = _view.id;
|
||||
this.workspace = _view.workspace;
|
||||
this.project = _view.project;
|
||||
this.filters = _view.filters ? this.computedFilters(_view.filters) : undefined;
|
||||
this.display_filters = _view.display_filters ? this.computedDisplayFilters(_view.display_filters) : undefined;
|
||||
this.display_properties = _view.display_properties
|
||||
? this.computedDisplayProperties(_view.display_properties)
|
||||
: undefined;
|
||||
this.user = _view.user;
|
||||
this.created_by = _view.created_by;
|
||||
this.updated_by = _view.updated_by;
|
||||
this.created_at = _view.created_at;
|
||||
this.updated_at = _view.updated_at;
|
||||
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
loader: observable,
|
||||
filtersToUpdate: observable.ref,
|
||||
// computed
|
||||
appliedFilters: computed,
|
||||
appliedFiltersQueryParams: computed,
|
||||
// helper actions
|
||||
updateFilters: action,
|
||||
updateDisplayFilters: action,
|
||||
updateDisplayProperties: action,
|
||||
resetFilterChanges: action,
|
||||
saveFilterChanges: action,
|
||||
// actions
|
||||
update: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
get appliedFilters() {
|
||||
return {
|
||||
filters: this.filters ? this.computedFilters(this.filters, this.filtersToUpdate.filters) : undefined,
|
||||
display_filters: this.display_filters
|
||||
? this.computedDisplayFilters(this.display_filters, this.filtersToUpdate.display_filters)
|
||||
: undefined,
|
||||
display_properties: this.display_properties
|
||||
? this.computedDisplayProperties(this.display_properties, this.filtersToUpdate.display_properties)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
get appliedFiltersQueryParams() {
|
||||
const filters = this.appliedFilters;
|
||||
if (!filters) return undefined;
|
||||
return this.computeAppliedFiltersQueryParameters(filters, [])?.query || undefined;
|
||||
}
|
||||
|
||||
// helper actions
|
||||
updateFilters = (filters: Partial<TViewFilters>) => {
|
||||
runInAction(() => {
|
||||
this.loader = "submit";
|
||||
this.filtersToUpdate.filters = filters;
|
||||
});
|
||||
};
|
||||
|
||||
updateDisplayFilters = async (display_filters: Partial<TViewDisplayFilters>) => {
|
||||
const appliedFilters = this.appliedFilters;
|
||||
|
||||
const layout = appliedFilters?.display_filters?.layout;
|
||||
const sub_group_by = appliedFilters?.display_filters?.sub_group_by;
|
||||
const group_by = appliedFilters?.display_filters?.group_by;
|
||||
const sub_issue = appliedFilters?.display_filters?.sub_issue;
|
||||
|
||||
if (group_by === undefined) display_filters.sub_group_by = undefined;
|
||||
if (layout === "kanban") {
|
||||
if (sub_group_by === group_by) display_filters.group_by = undefined;
|
||||
if (group_by === null) display_filters.group_by = "state";
|
||||
}
|
||||
if (layout === "spreadsheet" && sub_issue === true) display_filters.sub_issue = false;
|
||||
|
||||
runInAction(() => {
|
||||
this.loader = "submit";
|
||||
this.filtersToUpdate.display_filters = display_filters;
|
||||
});
|
||||
};
|
||||
|
||||
updateDisplayProperties = async (display_properties: Partial<TViewDisplayProperties>) => {
|
||||
runInAction(() => {
|
||||
this.loader = "submit";
|
||||
this.filtersToUpdate.display_properties = display_properties;
|
||||
});
|
||||
};
|
||||
|
||||
resetFilterChanges = () => {
|
||||
runInAction(() => {
|
||||
this.loader = undefined;
|
||||
this.filtersToUpdate = {
|
||||
filters: {},
|
||||
display_filters: {},
|
||||
display_properties: {},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
saveFilterChanges = async () => {
|
||||
this.loader = "submitting";
|
||||
if (this.appliedFilters) await this.update(this.appliedFilters);
|
||||
this.loader = undefined;
|
||||
};
|
||||
|
||||
// actions
|
||||
update = async (viewData: Partial<TViewFilterProps>) => {
|
||||
if (!this.workspaceSlug || !this.id) return;
|
||||
|
||||
const view = await this.service.update(this.workspaceSlug, viewData, this.projectId, this.featureId);
|
||||
if (!view) return;
|
||||
|
||||
runInAction(() => {
|
||||
Object.keys(viewData).forEach((key) => {
|
||||
const _key = key as keyof TViewFilterProps;
|
||||
set(this, _key, viewData[_key]);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
@ -1,20 +1,21 @@
|
||||
// types
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import set from "lodash/set";
|
||||
// stores
|
||||
import { RootStore } from "store/root.store";
|
||||
import { ViewsStore } from "./view.store";
|
||||
import { ViewStore } from "./view.store";
|
||||
// types
|
||||
import { TViewService } from "services/view/types";
|
||||
import { TView } from "@plane/types";
|
||||
import { set } from "lodash";
|
||||
|
||||
export type TLoader = "" | undefined;
|
||||
|
||||
type TViewRoot = {
|
||||
type TViewRootStore = {
|
||||
// observables
|
||||
viewMap: Record<string, ViewsStore>;
|
||||
viewMap: Record<string, ViewStore>;
|
||||
// computed
|
||||
viewIds: string[];
|
||||
// helper actions
|
||||
viewById: (viewId: string) => ViewStore | undefined;
|
||||
// actions
|
||||
fetch: () => Promise<void>;
|
||||
create: (view: Partial<TView>) => Promise<void>;
|
||||
@ -22,13 +23,13 @@ type TViewRoot = {
|
||||
duplicate: (viewId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export class ViewRoot implements TViewRoot {
|
||||
viewMap: Record<string, ViewsStore> = {};
|
||||
export class ViewRootStore implements TViewRootStore {
|
||||
viewMap: Record<string, ViewStore> = {};
|
||||
|
||||
constructor(private store: RootStore, private service: TViewService) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
viewMap: observable,
|
||||
viewMap: observable.ref,
|
||||
// computed
|
||||
viewIds: computed,
|
||||
// actions
|
||||
@ -44,15 +45,10 @@ export class ViewRoot implements TViewRoot {
|
||||
return Object.keys(this.viewMap);
|
||||
}
|
||||
|
||||
get views() {
|
||||
return Object.values(this.viewMap);
|
||||
}
|
||||
// helper actions
|
||||
viewById = (viewId: string) => this.viewMap?.[viewId] || undefined;
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description This method is used to fetch all the views
|
||||
* @returns
|
||||
*/
|
||||
fetch = async () => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
@ -62,16 +58,11 @@ export class ViewRoot implements TViewRoot {
|
||||
|
||||
runInAction(() => {
|
||||
views.forEach((view) => {
|
||||
set(this.viewMap, [view.id], new ViewsStore(this.store, view, this.service));
|
||||
if (view.id) set(this.viewMap, [view.id], new ViewStore(this.store, view, this.service));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to create a view
|
||||
* @param data: Partial<TView>
|
||||
* @returns
|
||||
*/
|
||||
create = async (data: Partial<TView>) => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
@ -80,40 +71,30 @@ export class ViewRoot implements TViewRoot {
|
||||
if (!view) return;
|
||||
|
||||
runInAction(() => {
|
||||
set(this.viewMap, [view.id], new ViewsStore(this.store, view, this.service));
|
||||
if (view.id) set(this.viewMap, [view.id], new ViewStore(this.store, view, this.service));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to remove a view
|
||||
* @param viewId: string
|
||||
* @returns
|
||||
*/
|
||||
remove = async (viewId: string) => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !viewId) return;
|
||||
|
||||
await this.service.remove(workspaceSlug, viewId, projectId);
|
||||
await this.service.remove?.(workspaceSlug, viewId, projectId);
|
||||
|
||||
runInAction(() => {
|
||||
delete this.viewMap[viewId];
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to duplicate a view
|
||||
* @param viewId: string
|
||||
* @returns
|
||||
*/
|
||||
duplicate = async (viewId: string) => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.service.duplicate) return;
|
||||
|
||||
const view = await this.service.duplicate(workspaceSlug, viewId, projectId);
|
||||
if (!view) return;
|
||||
|
||||
runInAction(() => {
|
||||
set(this.viewMap, [view.id], new ViewsStore(this.store, view, this.service));
|
||||
if (view.id) set(this.viewMap, [view.id], new ViewStore(this.store, view, this.service));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -6,29 +6,29 @@ import { RootStore } from "store/root.store";
|
||||
import { TViewService } from "services/view/types";
|
||||
import {
|
||||
TView,
|
||||
TFilters,
|
||||
TDisplayFilters,
|
||||
TDisplayProperties,
|
||||
TFilterProps,
|
||||
TFilterPartialProps,
|
||||
TViewFilters,
|
||||
TViewDisplayFilters,
|
||||
TViewDisplayProperties,
|
||||
TViewFilterProps,
|
||||
TViewFilterPartialProps,
|
||||
TViewAccess,
|
||||
} from "@plane/types";
|
||||
// helpers
|
||||
import { FiltersHelper } from "./filters_helpers";
|
||||
import { FiltersHelper } from "./helpers/filters_helpers";
|
||||
|
||||
type TLoader = "submitting" | "submit" | undefined;
|
||||
|
||||
export type TViewsStore = TView & {
|
||||
export type TViewStore = TView & {
|
||||
// observables
|
||||
loader: TLoader;
|
||||
filtersToUpdate: TFilterPartialProps;
|
||||
filtersToUpdate: TViewFilterPartialProps;
|
||||
// computed
|
||||
appliedFilters: TFilterProps | undefined;
|
||||
appliedFilters: TViewFilterProps | undefined;
|
||||
appliedFiltersQueryParams: string | undefined;
|
||||
// helper actions
|
||||
updateFilters: (filters: Partial<TFilters>) => void;
|
||||
updateDisplayFilters: (display_filters: Partial<TDisplayFilters>) => void;
|
||||
updateDisplayProperties: (display_properties: Partial<TDisplayProperties>) => void;
|
||||
updateFilters: (filters: Partial<TViewFilters>) => void;
|
||||
updateDisplayFilters: (display_filters: Partial<TViewDisplayFilters>) => void;
|
||||
updateDisplayProperties: (display_properties: Partial<TViewDisplayProperties>) => void;
|
||||
resetFilterChanges: () => void;
|
||||
saveFilterChanges: () => void;
|
||||
// actions
|
||||
@ -39,29 +39,29 @@ export type TViewsStore = TView & {
|
||||
update: (viewData: Partial<TView>) => Promise<void>;
|
||||
};
|
||||
|
||||
export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
id: string;
|
||||
workspace: string;
|
||||
export class ViewStore extends FiltersHelper implements TViewStore {
|
||||
id: string | undefined;
|
||||
workspace: string | undefined;
|
||||
project: string | undefined;
|
||||
name: string;
|
||||
description: string;
|
||||
query: string;
|
||||
filters: TFilters;
|
||||
display_filters: TDisplayFilters;
|
||||
display_properties: TDisplayProperties;
|
||||
access: TViewAccess;
|
||||
owned_by: string;
|
||||
sort_order: number;
|
||||
is_locked: boolean;
|
||||
is_pinned: boolean;
|
||||
is_favorite: boolean;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
name: string | undefined;
|
||||
description: string | undefined;
|
||||
query: string | undefined;
|
||||
filters: TViewFilters | undefined;
|
||||
display_filters: TViewDisplayFilters | undefined;
|
||||
display_properties: TViewDisplayProperties | undefined;
|
||||
access: TViewAccess | undefined;
|
||||
owned_by: string | undefined;
|
||||
sort_order: number | undefined;
|
||||
is_locked: boolean | undefined;
|
||||
is_pinned: boolean | undefined;
|
||||
is_favorite: boolean | undefined;
|
||||
created_by: string | undefined;
|
||||
updated_by: string | undefined;
|
||||
created_at: Date | undefined;
|
||||
updated_at: Date | undefined;
|
||||
|
||||
loader: TLoader = undefined;
|
||||
filtersToUpdate: TFilterPartialProps = {
|
||||
filtersToUpdate: TViewFilterPartialProps = {
|
||||
filters: {},
|
||||
display_filters: {},
|
||||
display_properties: {},
|
||||
@ -75,9 +75,11 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
this.name = _view.name;
|
||||
this.description = _view.description;
|
||||
this.query = _view.query;
|
||||
this.filters = this.computedFilters(_view.filters);
|
||||
this.display_filters = this.computedDisplayFilters(_view.display_filters);
|
||||
this.display_properties = this.computedDisplayProperties(_view.display_properties);
|
||||
this.filters = _view.filters ? this.computedFilters(_view.filters) : undefined;
|
||||
this.display_filters = _view.display_filters ? this.computedDisplayFilters(_view.display_filters) : undefined;
|
||||
this.display_properties = _view.display_properties
|
||||
? this.computedDisplayProperties(_view.display_properties)
|
||||
: undefined;
|
||||
this.access = _view.access;
|
||||
this.owned_by = _view.owned_by;
|
||||
this.sort_order = _view.sort_order;
|
||||
@ -112,51 +114,43 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
// computed
|
||||
get appliedFilters() {
|
||||
return {
|
||||
filters: this.computedFilters(this.filters, this.filtersToUpdate.filters),
|
||||
display_filters: this.computedDisplayFilters(this.display_filters, this.filtersToUpdate.display_filters),
|
||||
display_properties: this.computedDisplayProperties(
|
||||
this.display_properties,
|
||||
this.filtersToUpdate.display_properties
|
||||
),
|
||||
filters: this.filters ? this.computedFilters(this.filters, this.filtersToUpdate.filters) : undefined,
|
||||
display_filters: this.display_filters
|
||||
? this.computedDisplayFilters(this.display_filters, this.filtersToUpdate.display_filters)
|
||||
: undefined,
|
||||
display_properties: this.display_properties
|
||||
? this.computedDisplayProperties(this.display_properties, this.filtersToUpdate.display_properties)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
get appliedFiltersQueryParams() {
|
||||
const filters = this.appliedFilters;
|
||||
if (!filters) return undefined;
|
||||
return this.computeAppliedFiltersQueryParameters(filters, [])?.query || undefined;
|
||||
}
|
||||
|
||||
// helper actions
|
||||
/**
|
||||
* @description This method is used to update the filters of the view
|
||||
* @param filters: Partial<TFilters>
|
||||
*/
|
||||
updateFilters = (filters: Partial<TFilters>) => {
|
||||
updateFilters = (filters: Partial<TViewFilters>) => {
|
||||
runInAction(() => {
|
||||
this.loader = "submit";
|
||||
this.filtersToUpdate.filters = filters;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to update the display filters of the view
|
||||
* @param display_filters: Partial<TDisplayFilters>
|
||||
*/
|
||||
updateDisplayFilters = async (display_filters: Partial<TDisplayFilters>) => {
|
||||
updateDisplayFilters = async (display_filters: Partial<TViewDisplayFilters>) => {
|
||||
const appliedFilters = this.appliedFilters;
|
||||
|
||||
const layout = appliedFilters.display_filters.layout;
|
||||
const sub_group_by = appliedFilters.display_filters.sub_group_by;
|
||||
const group_by = appliedFilters.display_filters.group_by;
|
||||
const sub_issue = appliedFilters.display_filters.sub_issue;
|
||||
const layout = appliedFilters?.display_filters?.layout;
|
||||
const sub_group_by = appliedFilters?.display_filters?.sub_group_by;
|
||||
const group_by = appliedFilters?.display_filters?.group_by;
|
||||
const sub_issue = appliedFilters?.display_filters?.sub_issue;
|
||||
|
||||
if (group_by === undefined) display_filters.sub_group_by = undefined;
|
||||
|
||||
if (layout === "kanban") {
|
||||
if (sub_group_by === group_by) display_filters.group_by = undefined;
|
||||
if (group_by === null) display_filters.group_by = "state";
|
||||
}
|
||||
|
||||
if (layout === "spreadsheet" && sub_issue === true) display_filters.sub_issue = false;
|
||||
|
||||
runInAction(() => {
|
||||
@ -165,20 +159,13 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to update the display properties of the view
|
||||
* @param display_properties: Partial<TDisplayProperties>
|
||||
*/
|
||||
updateDisplayProperties = async (display_properties: Partial<TDisplayProperties>) => {
|
||||
updateDisplayProperties = async (display_properties: Partial<TViewDisplayProperties>) => {
|
||||
runInAction(() => {
|
||||
this.loader = "submit";
|
||||
this.filtersToUpdate.display_properties = display_properties;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to reset the changes made to the filters
|
||||
*/
|
||||
resetFilterChanges = () => {
|
||||
runInAction(() => {
|
||||
this.loader = undefined;
|
||||
@ -190,9 +177,6 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to save the changes made to the filters
|
||||
*/
|
||||
saveFilterChanges = async () => {
|
||||
this.loader = "submitting";
|
||||
if (this.appliedFilters) await this.update(this.appliedFilters);
|
||||
@ -200,13 +184,9 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
};
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description This method is used to update the view lock
|
||||
* @returns
|
||||
*/
|
||||
lockView = async () => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.id || !this.service.lock) return;
|
||||
|
||||
const view = await this.service.lock(workspaceSlug, this.id, projectId);
|
||||
if (!view) return;
|
||||
@ -216,13 +196,9 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to remove the view lock
|
||||
* @returns
|
||||
*/
|
||||
unlockView = async () => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.id || !this.service.unlock) return;
|
||||
|
||||
const view = await this.service.unlock(workspaceSlug, this.id, projectId);
|
||||
if (!view) return;
|
||||
@ -232,13 +208,9 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to update the view favorite
|
||||
* @returns
|
||||
*/
|
||||
makeFavorite = async () => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.id || !this.service.makeFavorite) return;
|
||||
|
||||
const view = await this.service.makeFavorite(workspaceSlug, this.id, projectId);
|
||||
if (!view) return;
|
||||
@ -248,13 +220,9 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to remove the view favorite
|
||||
* @returns
|
||||
*/
|
||||
removeFavorite = async () => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.id || !this.service.removeFavorite) return;
|
||||
|
||||
const view = await this.service.removeFavorite(workspaceSlug, this.id, projectId);
|
||||
if (!view) return;
|
||||
@ -264,13 +232,9 @@ export class ViewsStore extends FiltersHelper implements TViewsStore {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description This method is used to update the view
|
||||
* @param viewData
|
||||
*/
|
||||
update = async (viewData: Partial<TView>) => {
|
||||
const { workspaceSlug, projectId } = this.store.app.router;
|
||||
if (!workspaceSlug) return;
|
||||
if (!workspaceSlug || !this.id) return;
|
||||
|
||||
const view = await this.service.update(workspaceSlug, this.id, viewData, projectId);
|
||||
if (!view) return;
|
||||
|
Loading…
Reference in New Issue
Block a user