mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: filter saving store and ui updates
This commit is contained in:
parent
0fb531e4b7
commit
5d9393cfa6
@ -2,6 +2,7 @@ import { FC, Fragment, ReactNode, useRef, useState } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
import { MonitorDot } from "lucide-react";
|
import { MonitorDot } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
@ -21,10 +22,20 @@ type TViewDisplayFiltersDropdown = {
|
|||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
displayDropdownText?: boolean;
|
displayDropdownText?: boolean;
|
||||||
|
dropdownPlacement?: Placement;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = observer((props) => {
|
export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, viewOperations, children, displayDropdownText = true } = props;
|
const {
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
viewId,
|
||||||
|
viewType,
|
||||||
|
viewOperations,
|
||||||
|
children,
|
||||||
|
displayDropdownText = true,
|
||||||
|
dropdownPlacement = "bottom-start",
|
||||||
|
} = props;
|
||||||
// state
|
// state
|
||||||
const [dropdownToggle, setDropdownToggle] = useState(false);
|
const [dropdownToggle, setDropdownToggle] = useState(false);
|
||||||
// refs
|
// refs
|
||||||
@ -34,7 +45,7 @@ export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = obser
|
|||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
// popper-js init
|
// popper-js init
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
placement: "bottom-start",
|
placement: dropdownPlacement,
|
||||||
modifiers: [
|
modifiers: [
|
||||||
{
|
{
|
||||||
name: "preventOverflow",
|
name: "preventOverflow",
|
||||||
@ -42,6 +53,12 @@ export const ViewDisplayFiltersDropdown: FC<TViewDisplayFiltersDropdown> = obser
|
|||||||
padding: 12,
|
padding: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "offset",
|
||||||
|
options: {
|
||||||
|
offset: [0, 10],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
// hooks
|
|
||||||
import { useProject, useProjectState, useMember } from "hooks/store";
|
|
||||||
// types
|
|
||||||
import { TViewFilters } from "@plane/types";
|
|
||||||
|
|
||||||
type TFilterPropertyItemByFilterKeyAndId = {
|
|
||||||
key: keyof TViewFilters;
|
|
||||||
id: string;
|
|
||||||
icon: string;
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const filterPropertyItemByFilterKeyAndId = (
|
|
||||||
key: keyof TViewFilters,
|
|
||||||
id: string
|
|
||||||
): TFilterPropertyItemByFilterKeyAndId | undefined => {
|
|
||||||
if (!key || id) return undefined;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case "project":
|
|
||||||
return undefined; // store
|
|
||||||
case "module":
|
|
||||||
return undefined; // store
|
|
||||||
case "cycle":
|
|
||||||
return undefined; // store
|
|
||||||
case "priority":
|
|
||||||
return undefined; // constant
|
|
||||||
case "state":
|
|
||||||
return undefined; // store
|
|
||||||
case "state_group":
|
|
||||||
return undefined; // constant
|
|
||||||
case "assignees":
|
|
||||||
return undefined; // store -> workspace and project level
|
|
||||||
case "mentions":
|
|
||||||
return undefined; // store -> workspace and project level
|
|
||||||
case "subscriber":
|
|
||||||
return undefined; // store -> workspace and project level
|
|
||||||
case "created_by":
|
|
||||||
return undefined; // store -> workspace and project level
|
|
||||||
case "labels":
|
|
||||||
return undefined; // store -> workspace and project level
|
|
||||||
case "start_date":
|
|
||||||
return undefined; // constants
|
|
||||||
case "target_date":
|
|
||||||
return undefined; // constants
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,13 +1,12 @@
|
|||||||
export * from "./all-issues-root";
|
export * from "./root";
|
||||||
|
|
||||||
// views
|
// views
|
||||||
export * from "./views/root";
|
export * from "./views/root";
|
||||||
export * from "./views/view-item";
|
export * from "./views/view-item";
|
||||||
|
export * from "./views/view-dropdown";
|
||||||
export * from "./views/dropdown/root";
|
export * from "./views/view-dropdown-item";
|
||||||
export * from "./views/dropdown/dropdown-item";
|
|
||||||
|
|
||||||
export * from "./views/create-edit-form";
|
export * from "./views/create-edit-form";
|
||||||
|
export * from "./views/edit-dropdown";
|
||||||
|
|
||||||
// layouts
|
// layouts
|
||||||
export * from "./layout";
|
export * from "./layout";
|
||||||
|
@ -6,7 +6,7 @@ import { useViewDetail } from "hooks/store";
|
|||||||
// ui
|
// ui
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { TViewTypes } from "@plane/types";
|
import { TViewLayouts, TViewTypes } from "@plane/types";
|
||||||
import { TViewOperations } from "./types";
|
import { TViewOperations } from "./types";
|
||||||
|
|
||||||
type TViewLayoutRoot = {
|
type TViewLayoutRoot = {
|
||||||
@ -17,7 +17,7 @@ type TViewLayoutRoot = {
|
|||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
};
|
};
|
||||||
|
|
||||||
const LAYOUTS_DATA: { key: string; title: string; icon: LucideIcon }[] = [
|
const LAYOUTS_DATA: { key: TViewLayouts; title: string; icon: LucideIcon }[] = [
|
||||||
{ key: "list", title: "List Layout", icon: List },
|
{ key: "list", title: "List Layout", icon: List },
|
||||||
{ key: "kanban", title: "Kanban Layout", icon: Kanban },
|
{ key: "kanban", title: "Kanban Layout", icon: Kanban },
|
||||||
{ key: "calendar", title: "Calendar Layout", icon: Calendar },
|
{ key: "calendar", title: "Calendar Layout", icon: Calendar },
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { FC, Fragment, useEffect, useMemo, useState } from "react";
|
import { FC, Fragment, useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { CheckCircle, ChevronDown, ChevronUp, Pencil } from "lucide-react";
|
import { CheckCircle, Pencil } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useView, useViewDetail } from "hooks/store";
|
import { useView, useViewDetail } from "hooks/store";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
ViewAppliedFiltersRoot,
|
ViewAppliedFiltersRoot,
|
||||||
ViewDuplicateConfirmationModal,
|
ViewDuplicateConfirmationModal,
|
||||||
ViewDeleteConfirmationModal,
|
ViewDeleteConfirmationModal,
|
||||||
|
ViewEditDropdown,
|
||||||
} from ".";
|
} from ".";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
@ -24,7 +25,7 @@ import { viewLocalPayload } from "constants/view";
|
|||||||
import { TViewOperations } from "./types";
|
import { TViewOperations } from "./types";
|
||||||
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties, TViewTypes } from "@plane/types";
|
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties, TViewTypes } from "@plane/types";
|
||||||
|
|
||||||
type TAllIssuesViewRoot = {
|
type TGlobalViewRoot = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string | undefined;
|
projectId: string | undefined;
|
||||||
viewId: string;
|
viewId: string;
|
||||||
@ -38,7 +39,7 @@ type TViewOperationsToggle = {
|
|||||||
viewId: string | undefined;
|
viewId: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, viewId, viewType, baseRoute, workspaceViewTabOptions } = props;
|
const { workspaceSlug, projectId, viewId, viewType, baseRoute, workspaceViewTabOptions } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewStore = useView(workspaceSlug, projectId, viewType);
|
const viewStore = useView(workspaceSlug, projectId, viewType);
|
||||||
@ -112,7 +113,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
[viewStore, viewDetailStore, setToastAlert, viewOperationsToggle, viewDetailCreateStore]
|
[viewStore, viewDetailStore, setToastAlert, viewOperationsToggle, viewDetailCreateStore]
|
||||||
);
|
);
|
||||||
|
|
||||||
// fetch all issues
|
// fetch all views
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchViews = async () => {
|
const fetchViews = async () => {
|
||||||
await viewStore?.fetch(viewStore?.viewIds.length > 0 ? "mutation-loader" : "init-loader");
|
await viewStore?.fetch(viewStore?.viewIds.length > 0 ? "mutation-loader" : "init-loader");
|
||||||
@ -130,28 +131,33 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full">
|
<div className="relative w-full h-full">
|
||||||
<div className="relative flex justify-between items-center gap-2 px-5 py-4">
|
<div className="relative flex items-center gap-2 px-5 py-4">
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2 overflow-hidden">
|
||||||
<div className="flex-shrink-0 w-6 h-6 rounded relative flex justify-center items-center bg-custom-background-80">
|
<div className="flex-shrink-0 w-6 h-6 rounded relative flex justify-center items-center bg-custom-background-80">
|
||||||
<CheckCircle size={12} />
|
<CheckCircle size={12} />
|
||||||
</div>
|
</div>
|
||||||
<div className="font-medium">All Issues</div>
|
<div className="font-medium inline-block whitespace-nowrap overflow-hidden truncate line-clamp-1">
|
||||||
|
All Issues
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative inline-flex items-center rounded border border-custom-border-200 bg-custom-background-80">
|
|
||||||
{workspaceViewTabOptions.map((tab) => (
|
<div className="ml-auto relative flex items-center gap-3">
|
||||||
<Link
|
<div className="relative flex items-center rounded border border-custom-border-200 bg-custom-background-80">
|
||||||
key={tab.key}
|
{workspaceViewTabOptions.map((tab) => (
|
||||||
href={tab.href}
|
<Link
|
||||||
className={`p-4 py-1.5 rounded text-sm transition-all cursor-pointer font-medium
|
key={tab.key}
|
||||||
|
href={tab.href}
|
||||||
|
className={`p-4 py-1.5 rounded text-sm transition-all cursor-pointer font-medium
|
||||||
${
|
${
|
||||||
viewType === tab.key
|
viewType === tab.key
|
||||||
? "text-custom-text-100 bg-custom-background-100"
|
? "text-custom-text-100 bg-custom-background-100"
|
||||||
: "text-custom-text-200 bg-custom-background-80 hover:text-custom-text-100"
|
: "text-custom-text-200 bg-custom-background-80 hover:text-custom-text-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab.title}
|
{tab.title}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -201,7 +207,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
displayDropdownText={true}
|
displayDropdownText={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -212,7 +218,7 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
viewOperations={viewOperations}
|
viewOperations={viewOperations}
|
||||||
displayDropdownText={true}
|
displayDropdownText={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -222,14 +228,13 @@ export const AllIssuesViewRoot: FC<TAllIssuesViewRoot> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className=" relative flex items-center rounded h-7 transition-all cursor-pointer bg-custom-primary-100/20 text-custom-primary-100">
|
<ViewEditDropdown
|
||||||
<div className="text-sm px-3 font-medium h-full border-r border-white/50 flex justify-center items-center rounded-l transition-all hover:bg-custom-primary-100/30">
|
workspaceSlug={workspaceSlug}
|
||||||
Update
|
projectId={projectId}
|
||||||
</div>
|
viewId={viewId}
|
||||||
<div className="flex-shrink-0 px-1.5 hover:bg-custom-primary-100/30 h-full flex justify-center items-center rounded-r transition-all">
|
viewType={viewType}
|
||||||
<ChevronDown size={16} />
|
viewOperations={viewOperations}
|
||||||
</div>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
41
web/components/view/views/edit-dropdown.tsx
Normal file
41
web/components/view/views/edit-dropdown.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// hooks
|
||||||
|
import { useViewDetail } from "hooks/store";
|
||||||
|
// components
|
||||||
|
// types
|
||||||
|
import { TViewTypes } from "@plane/types";
|
||||||
|
import { TViewOperations } from "../types";
|
||||||
|
|
||||||
|
type TViewEditDropdown = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string | undefined;
|
||||||
|
viewId: string;
|
||||||
|
viewType: TViewTypes;
|
||||||
|
viewOperations: TViewOperations;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewEditDropdown: FC<TViewEditDropdown> = observer((props) => {
|
||||||
|
const { workspaceSlug, projectId, viewId, viewType, viewOperations } = props;
|
||||||
|
// hooks
|
||||||
|
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
|
||||||
|
|
||||||
|
if (!viewDetailStore?.isFiltersUpdateEnabled) return <></>;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className=" relative flex items-center rounded h-7 transition-all cursor-pointer bg-custom-primary-100/20 text-custom-primary-100">
|
||||||
|
<button
|
||||||
|
className="text-sm px-3 font-medium h-full border-r border-white/50 flex justify-center items-center rounded-l transition-all hover:bg-custom-primary-100/30"
|
||||||
|
disabled={viewDetailStore?.loader === "filters_submitting"}
|
||||||
|
onClick={() => viewOperations.update()}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
<div className="flex-shrink-0 px-1.5 hover:bg-custom-primary-100/30 h-full flex justify-center items-center rounded-r transition-all">
|
||||||
|
<ChevronDown size={16} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -1,15 +1,16 @@
|
|||||||
import { FC, Fragment, ReactNode, useRef, useState } from "react";
|
import { FC, Fragment, ReactNode, useRef, useState } from "react";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
|
import { Placement } from "@popperjs/core";
|
||||||
import { Plus, Search } from "lucide-react";
|
import { Plus, Search } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
import { useView } from "hooks/store";
|
import { useView } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { ViewDropdownItem } from "../../";
|
import { ViewDropdownItem } from "..";
|
||||||
// types
|
// types
|
||||||
import { TViewTypes } from "@plane/types";
|
import { TViewTypes } from "@plane/types";
|
||||||
import { TViewOperations } from "../../types";
|
import { TViewOperations } from "../types";
|
||||||
|
|
||||||
type TViewDropdown = {
|
type TViewDropdown = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -19,10 +20,20 @@ type TViewDropdown = {
|
|||||||
viewOperations: TViewOperations;
|
viewOperations: TViewOperations;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
baseRoute: string;
|
baseRoute: string;
|
||||||
|
dropdownPlacement?: Placement;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
||||||
const { workspaceSlug, projectId, viewId: currentViewId, viewType, viewOperations, children, baseRoute } = props;
|
const {
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
viewId: currentViewId,
|
||||||
|
viewType,
|
||||||
|
viewOperations,
|
||||||
|
children,
|
||||||
|
baseRoute,
|
||||||
|
dropdownPlacement = "bottom-start",
|
||||||
|
} = props;
|
||||||
// hooks
|
// hooks
|
||||||
const viewStore = useView(workspaceSlug, projectId, viewType);
|
const viewStore = useView(workspaceSlug, projectId, viewType);
|
||||||
// states
|
// states
|
||||||
@ -35,7 +46,7 @@ export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
|||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
// popper-js init
|
// popper-js init
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
placement: "bottom-start",
|
placement: dropdownPlacement,
|
||||||
modifiers: [
|
modifiers: [
|
||||||
{
|
{
|
||||||
name: "preventOverflow",
|
name: "preventOverflow",
|
||||||
@ -43,6 +54,12 @@ export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
|||||||
padding: 12,
|
padding: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "offset",
|
||||||
|
options: {
|
||||||
|
offset: [0, 10],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -80,7 +97,7 @@ export const ViewDropdown: FC<TViewDropdown> = (props) => {
|
|||||||
ref={setPopperElement}
|
ref={setPopperElement}
|
||||||
style={styles.popper}
|
style={styles.popper}
|
||||||
{...attributes.popper}
|
{...attributes.popper}
|
||||||
className="my-1 w-64 p-2 space-y-2 rounded bg-custom-background-100 border-[0.5px] border-custom-border-300 shadow-custom-shadow-rg focus:outline-none"
|
className="w-64 p-2 space-y-2 rounded bg-custom-background-100 border-[0.5px] border-custom-border-300 shadow-custom-shadow-rg focus:outline-none"
|
||||||
>
|
>
|
||||||
<div className="relative p-0.5 px-2 text-sm flex items-center gap-2 rounded border border-custom-border-100 bg-custom-background-90">
|
<div className="relative p-0.5 px-2 text-sm flex items-center gap-2 rounded border border-custom-border-100 bg-custom-background-90">
|
||||||
<Search className="h-3 w-3 text-custom-text-300" strokeWidth={1.5} />
|
<Search className="h-3 w-3 text-custom-text-300" strokeWidth={1.5} />
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { AllIssuesViewRoot } from "components/view";
|
import { GlobalViewRoot } from "components/view";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
@ -33,7 +33,7 @@ const WorkspacePublicViewPage: NextPageWithLayout = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
|
<div className="w-full h-full overflow-hidden bg-custom-background-100 relative flex flex-col">
|
||||||
<div className="flex-shrink-0 w-full">
|
<div className="flex-shrink-0 w-full">
|
||||||
<AllIssuesViewRoot
|
<GlobalViewRoot
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={undefined}
|
projectId={undefined}
|
||||||
viewId={viewId.toString()}
|
viewId={viewId.toString()}
|
||||||
|
@ -3,6 +3,7 @@ import set from "lodash/set";
|
|||||||
import update from "lodash/update";
|
import update from "lodash/update";
|
||||||
import concat from "lodash/concat";
|
import concat from "lodash/concat";
|
||||||
import pull from "lodash/pull";
|
import pull from "lodash/pull";
|
||||||
|
import isEqual from "lodash/isEqual";
|
||||||
// store
|
// store
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
// types
|
// types
|
||||||
@ -18,7 +19,7 @@ import {
|
|||||||
// helpers
|
// helpers
|
||||||
import { FiltersHelper } from "./helpers/filters_helpers";
|
import { FiltersHelper } from "./helpers/filters_helpers";
|
||||||
|
|
||||||
type TLoader = "submitting" | "submit" | undefined;
|
type TLoader = "filters_submit" | "filters_submitting" | "update" | "updating" | undefined;
|
||||||
|
|
||||||
export type TViewStore = TView & {
|
export type TViewStore = TView & {
|
||||||
// observables
|
// observables
|
||||||
@ -28,6 +29,7 @@ export type TViewStore = TView & {
|
|||||||
appliedFilters: TViewFilterProps | undefined;
|
appliedFilters: TViewFilterProps | undefined;
|
||||||
appliedFiltersQueryParams: string | undefined;
|
appliedFiltersQueryParams: string | undefined;
|
||||||
isFiltersApplied: boolean;
|
isFiltersApplied: boolean;
|
||||||
|
isFiltersUpdateEnabled: boolean;
|
||||||
// helper actions
|
// helper actions
|
||||||
setName: (name: string) => void;
|
setName: (name: string) => void;
|
||||||
setDescription: (description: string) => void;
|
setDescription: (description: string) => void;
|
||||||
@ -37,11 +39,11 @@ export type TViewStore = TView & {
|
|||||||
resetChanges: () => void;
|
resetChanges: () => void;
|
||||||
saveChanges: () => Promise<void>;
|
saveChanges: () => Promise<void>;
|
||||||
// actions
|
// actions
|
||||||
|
update: (viewData: Partial<TView>) => Promise<void>;
|
||||||
lockView: () => Promise<void>;
|
lockView: () => Promise<void>;
|
||||||
unlockView: () => Promise<void>;
|
unlockView: () => Promise<void>;
|
||||||
makeFavorite: () => Promise<void>;
|
makeFavorite: () => Promise<void>;
|
||||||
removeFavorite: () => Promise<void>;
|
removeFavorite: () => Promise<void>;
|
||||||
update: (viewData: Partial<TView>) => Promise<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ViewStore extends FiltersHelper implements TViewStore {
|
export class ViewStore extends FiltersHelper implements TViewStore {
|
||||||
@ -102,10 +104,14 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
this.updated_by = _view.updated_by;
|
this.updated_by = _view.updated_by;
|
||||||
this.created_at = _view.created_at;
|
this.created_at = _view.created_at;
|
||||||
this.updated_at = _view.updated_at;
|
this.updated_at = _view.updated_at;
|
||||||
|
|
||||||
this.is_local_view = _view.is_local_view;
|
this.is_local_view = _view.is_local_view;
|
||||||
this.is_create = _view.is_create;
|
this.is_create = _view.is_create;
|
||||||
this.is_editable = _view.is_editable;
|
this.is_editable = _view.is_editable;
|
||||||
|
this.filtersToUpdate = {
|
||||||
|
filters: this.computedFilters(_view.filters),
|
||||||
|
display_filters: this.computedDisplayFilters(_view.display_filters),
|
||||||
|
display_properties: this.computedDisplayProperties(_view.display_properties),
|
||||||
|
};
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observables
|
// observables
|
||||||
@ -137,6 +143,7 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
appliedFilters: computed,
|
appliedFilters: computed,
|
||||||
appliedFiltersQueryParams: computed,
|
appliedFiltersQueryParams: computed,
|
||||||
isFiltersApplied: computed,
|
isFiltersApplied: computed,
|
||||||
|
isFiltersUpdateEnabled: computed,
|
||||||
// helper actions
|
// helper actions
|
||||||
setName: action,
|
setName: action,
|
||||||
setFilters: action,
|
setFilters: action,
|
||||||
@ -148,6 +155,8 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
update: action,
|
update: action,
|
||||||
lockView: action,
|
lockView: action,
|
||||||
unlockView: action,
|
unlockView: action,
|
||||||
|
makeFavorite: action,
|
||||||
|
removeFavorite: action,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,6 +188,17 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
return isFiltersApplied;
|
return isFiltersApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isFiltersUpdateEnabled() {
|
||||||
|
const filters = this.filters;
|
||||||
|
const appliedFilters = this.appliedFilters?.filters;
|
||||||
|
let isFiltersUpdateEnabled = false;
|
||||||
|
Object.keys(appliedFilters).forEach((key) => {
|
||||||
|
const _key = key as keyof TViewFilters;
|
||||||
|
if (!isEqual(appliedFilters[_key].slice().sort(), filters[_key].slice().sort())) isFiltersUpdateEnabled = true;
|
||||||
|
});
|
||||||
|
return isFiltersUpdateEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
// helper actions
|
// helper actions
|
||||||
setName = (name: string) => {
|
setName = (name: string) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
@ -194,10 +214,8 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
|
|
||||||
setFilters = (filterKey: keyof TViewFilters | undefined = undefined, filterValue: "clear_all" | string) => {
|
setFilters = (filterKey: keyof TViewFilters | undefined = undefined, filterValue: "clear_all" | string) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = "submit";
|
|
||||||
if (filterKey === undefined) {
|
if (filterKey === undefined) {
|
||||||
if (filterValue === "clear_all") set(this.filtersToUpdate, ["filters"], {});
|
if (filterValue === "clear_all") set(this.filtersToUpdate, ["filters"], {});
|
||||||
this.loader = undefined;
|
|
||||||
} else
|
} else
|
||||||
update(this.filtersToUpdate, ["filters", filterKey], (_values = []) => {
|
update(this.filtersToUpdate, ["filters", filterKey], (_values = []) => {
|
||||||
if (filterValue === "clear_all") return [];
|
if (filterValue === "clear_all") return [];
|
||||||
@ -238,7 +256,6 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
|
|
||||||
resetChanges = () => {
|
resetChanges = () => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = undefined;
|
|
||||||
this.filtersToUpdate = {
|
this.filtersToUpdate = {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
@ -251,11 +268,8 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
|
|
||||||
saveChanges = async () => {
|
saveChanges = async () => {
|
||||||
try {
|
try {
|
||||||
this.loader = "submitting";
|
|
||||||
if (this.filtersToUpdate) await this.update(this.filtersToUpdate);
|
if (this.filtersToUpdate) await this.update(this.filtersToUpdate);
|
||||||
this.loader = undefined;
|
|
||||||
} catch {
|
} catch {
|
||||||
this.loader = undefined;
|
|
||||||
Object.keys(this.filtersToUpdate).forEach((key) => {
|
Object.keys(this.filtersToUpdate).forEach((key) => {
|
||||||
const _key = key as keyof TView;
|
const _key = key as keyof TView;
|
||||||
set(this, _key, this.filtersToUpdate[_key]);
|
set(this, _key, this.filtersToUpdate[_key]);
|
||||||
@ -264,6 +278,25 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
|
update = async (viewData: Partial<TView>) => {
|
||||||
|
try {
|
||||||
|
const { workspaceSlug, projectId } = this.store.app.router;
|
||||||
|
if (!workspaceSlug || !this.id) return;
|
||||||
|
|
||||||
|
const view = await this.service.update(workspaceSlug, this.id, viewData, projectId);
|
||||||
|
if (!view) return;
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
Object.keys(viewData).forEach((key) => {
|
||||||
|
const _key = key as keyof TView;
|
||||||
|
set(this, _key, viewData[_key]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
this.resetChanges();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
lockView = async () => {
|
lockView = async () => {
|
||||||
try {
|
try {
|
||||||
const { workspaceSlug, projectId } = this.store.app.router;
|
const { workspaceSlug, projectId } = this.store.app.router;
|
||||||
@ -327,23 +360,4 @@ export class ViewStore extends FiltersHelper implements TViewStore {
|
|||||||
this.is_favorite = this.is_favorite;
|
this.is_favorite = this.is_favorite;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
update = async (viewData: Partial<TView>) => {
|
|
||||||
try {
|
|
||||||
const { workspaceSlug, projectId } = this.store.app.router;
|
|
||||||
if (!workspaceSlug || !this.id) return;
|
|
||||||
|
|
||||||
const view = await this.service.update(workspaceSlug, this.id, viewData, projectId);
|
|
||||||
if (!view) return;
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(viewData).forEach((key) => {
|
|
||||||
const _key = key as keyof TView;
|
|
||||||
set(this, _key, viewData[_key]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
this.resetChanges();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
18
yarn.lock
18
yarn.lock
@ -5012,7 +5012,7 @@ fault@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
format "^0.2.0"
|
format "^0.2.0"
|
||||||
|
|
||||||
fflate@^0.4.1:
|
fflate@^0.4.8:
|
||||||
version "0.4.8"
|
version "0.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
|
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
|
||||||
integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
|
integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
|
||||||
@ -7171,12 +7171,18 @@ postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.29:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
posthog-js@^1.88.4:
|
posthog-js@^1.105.0:
|
||||||
version "1.96.1"
|
version "1.105.6"
|
||||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.96.1.tgz#4f9719a24e4e14037b0e72d430194d7cdb576447"
|
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.105.6.tgz#3544de4389d5c7743fa420178bd127e49c4dc825"
|
||||||
integrity sha512-kv1vQqYMt2BV3YHS+wxsbGuP+tz+M3y1AzNhz8TfkpY1HT8W/ONT0i0eQpeRr9Y+d4x/fZ6M4cXG5GMvi9lRCA==
|
integrity sha512-5ITXsh29XIuNohHLy21nawGnfFZDpyt+yfnWge9sJl5yv0nNuoUmLiDgw1tJafoqGrfd5CUasKyzSI21HxsSeQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
fflate "^0.4.1"
|
fflate "^0.4.8"
|
||||||
|
preact "^10.19.3"
|
||||||
|
|
||||||
|
preact@^10.19.3:
|
||||||
|
version "10.19.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/preact/-/preact-10.19.4.tgz#735d331d5b1bd2182cc36f2ba481fd6f0da3fe3b"
|
||||||
|
integrity sha512-dwaX5jAh0Ga8uENBX1hSOujmKWgx9RtL80KaKUFLc6jb4vCEAc3EeZ0rnQO/FO4VgjfPMfoLFWnNG8bHuZ9VLw==
|
||||||
|
|
||||||
prebuild-install@^7.1.1:
|
prebuild-install@^7.1.1:
|
||||||
version "7.1.1"
|
version "7.1.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user