fix: manage widgets integrations (#6331)
* wip * chore: wip * fix: preserved old component * fix * fix: seperate route added * fix * Only return user ID of project members * Return issue ID * fix: recents api integrations * fix: types * fix: types * fix: added tooltips * chore: added apis * fix: widgets fix * fix: lint * fix: integrated dashboard apis * fix: added ee dummy component for header * fix: removed logs --------- Co-authored-by: sangeethailango <sangeethailango21@gmail.com>
This commit is contained in:
parent
edb68a1bc6
commit
0af9b09844
16 changed files with 110 additions and 74 deletions
4
packages/types/src/home.d.ts
vendored
4
packages/types/src/home.d.ts
vendored
|
|
@ -2,7 +2,7 @@ import { TLogoProps } from "./common";
|
||||||
import { TIssuePriorities } from "./issues";
|
import { TIssuePriorities } from "./issues";
|
||||||
|
|
||||||
export type TRecentActivityFilterKeys = "all item" | "issue" | "page" | "project";
|
export type TRecentActivityFilterKeys = "all item" | "issue" | "page" | "project";
|
||||||
export type THomeWidgetKeys = "quick_links" | "recent_activity" | "stickies";
|
export type THomeWidgetKeys = "quick_links" | "recents" | "my_stickies" | "quick_tutorial" | "new_at_plane";
|
||||||
|
|
||||||
export type THomeWidgetProps = {
|
export type THomeWidgetProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -69,7 +69,7 @@ export type TLinkIdMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TWidgetEntityData = {
|
export type TWidgetEntityData = {
|
||||||
key: string;
|
key: THomeWidgetKeys;
|
||||||
name: string;
|
name: string;
|
||||||
is_enabled: boolean;
|
is_enabled: boolean;
|
||||||
sort_order: number;
|
sort_order: number;
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,20 @@ import { DashboardQuickLinks } from "./widgets/links";
|
||||||
import { ManageWidgetsModal } from "./widgets/manage";
|
import { ManageWidgetsModal } from "./widgets/manage";
|
||||||
|
|
||||||
const WIDGETS_LIST: {
|
const WIDGETS_LIST: {
|
||||||
[key in THomeWidgetKeys]: { component: React.FC<THomeWidgetProps>; fullWidth: boolean };
|
[key in THomeWidgetKeys]: { component: React.FC<THomeWidgetProps> | null; fullWidth: boolean };
|
||||||
} = {
|
} = {
|
||||||
quick_links: { component: DashboardQuickLinks, fullWidth: false },
|
quick_links: { component: DashboardQuickLinks, fullWidth: false },
|
||||||
recent_activity: { component: RecentActivityWidget, fullWidth: false },
|
recents: { component: RecentActivityWidget, fullWidth: false },
|
||||||
stickies: { component: StickiesWidget, fullWidth: false },
|
my_stickies: { component: StickiesWidget, fullWidth: false },
|
||||||
|
new_at_plane: { component: null, fullWidth: false },
|
||||||
|
quick_tutorial: { component: null, fullWidth: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DashboardWidgets = observer(() => {
|
export const DashboardWidgets = observer(() => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleWidgetSettings, showWidgetSettings } = useHome();
|
const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets } = useHome();
|
||||||
|
|
||||||
if (!workspaceSlug) return null;
|
if (!workspaceSlug) return null;
|
||||||
|
|
||||||
|
|
@ -36,9 +38,11 @@ export const DashboardWidgets = observer(() => {
|
||||||
handleOnClose={() => toggleWidgetSettings(false)}
|
handleOnClose={() => toggleWidgetSettings(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{Object.entries(WIDGETS_LIST).map(([key, widget]) => {
|
{orderedWidgets.map((key) => {
|
||||||
const WidgetComponent = widget.component;
|
const WidgetComponent = WIDGETS_LIST[key]?.component;
|
||||||
if (widget.fullWidth)
|
const isEnabled = widgetsMap[key]?.is_enabled;
|
||||||
|
if (!WidgetComponent || !isEnabled) return null;
|
||||||
|
if (WIDGETS_LIST[key]?.fullWidth)
|
||||||
return (
|
return (
|
||||||
<div key={key} className="lg:col-span-2">
|
<div key={key} className="lg:col-span-2">
|
||||||
<WidgetComponent workspaceSlug={workspaceSlug.toString()} />
|
<WidgetComponent workspaceSlug={workspaceSlug.toString()} />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect } from "react";
|
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// components
|
// components
|
||||||
|
import useSWR from "swr";
|
||||||
import { ContentWrapper } from "@plane/ui";
|
import { ContentWrapper } from "@plane/ui";
|
||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
import { TourRoot } from "@/components/onboarding";
|
import { TourRoot } from "@/components/onboarding";
|
||||||
|
|
@ -11,7 +11,7 @@ import { PRODUCT_TOUR_COMPLETED } from "@/constants/event-tracker";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCommandPalette, useUserProfile, useEventTracker, useDashboard, useProject, useUser } from "@/hooks/store";
|
import { useCommandPalette, useUserProfile, useEventTracker, useProject, useUser } from "@/hooks/store";
|
||||||
import { useHome } from "@/hooks/store/use-home";
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
import useSize from "@/hooks/use-window-size";
|
import useSize from "@/hooks/use-window-size";
|
||||||
import { IssuePeekOverview } from "../issues";
|
import { IssuePeekOverview } from "../issues";
|
||||||
|
|
@ -29,12 +29,20 @@ export const WorkspaceHomeView = observer(() => {
|
||||||
const { data: currentUser } = useUser();
|
const { data: currentUser } = useUser();
|
||||||
const { data: currentUserProfile, updateTourCompleted } = useUserProfile();
|
const { data: currentUserProfile, updateTourCompleted } = useUserProfile();
|
||||||
const { captureEvent } = useEventTracker();
|
const { captureEvent } = useEventTracker();
|
||||||
const { homeDashboardId, fetchHomeDashboardWidgets } = useDashboard();
|
const { toggleWidgetSettings, fetchWidgets } = useHome();
|
||||||
const { toggleWidgetSettings } = useHome();
|
|
||||||
const { joinedProjectIds, loader } = useProject();
|
const { joinedProjectIds, loader } = useProject();
|
||||||
|
|
||||||
const [windowWidth] = useSize();
|
const [windowWidth] = useSize();
|
||||||
|
|
||||||
|
useSWR(
|
||||||
|
workspaceSlug ? `HOME_DASHBOARD_WIDGETS_${workspaceSlug}` : null,
|
||||||
|
workspaceSlug ? () => fetchWidgets(workspaceSlug?.toString()) : null,
|
||||||
|
{
|
||||||
|
revalidateIfStale: true,
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
revalidateOnReconnect: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const handleTourCompleted = () => {
|
const handleTourCompleted = () => {
|
||||||
updateTourCompleted()
|
updateTourCompleted()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -48,13 +56,6 @@ export const WorkspaceHomeView = observer(() => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// fetch home dashboard widgets on workspace change
|
|
||||||
useEffect(() => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
|
|
||||||
fetchHomeDashboardWidgets(workspaceSlug?.toString());
|
|
||||||
}, [fetchHomeDashboardWidgets, workspaceSlug]);
|
|
||||||
|
|
||||||
// TODO: refactor loader implementation
|
// TODO: refactor loader implementation
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -63,7 +64,7 @@ export const WorkspaceHomeView = observer(() => {
|
||||||
<TourRoot onComplete={handleTourCompleted} />
|
<TourRoot onComplete={handleTourCompleted} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{homeDashboardId && joinedProjectIds && (
|
{joinedProjectIds && (
|
||||||
<>
|
<>
|
||||||
{joinedProjectIds.length > 0 || loader ? (
|
{joinedProjectIds.length > 0 || loader ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,13 @@ export const UserGreetingsView: FC<IUserGreetingsView> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</h6>
|
</h6>
|
||||||
</div>
|
</div>
|
||||||
{/* <button
|
<button
|
||||||
onClick={handleWidgetModal}
|
onClick={handleWidgetModal}
|
||||||
className="flex items-center gap-2 font-medium text-custom-text-300 justify-center border border-custom-border-200 rounded p-2 my-auto mb-0"
|
className="flex items-center gap-2 font-medium text-custom-text-300 justify-center border border-custom-border-200 rounded p-2 my-auto mb-0"
|
||||||
>
|
>
|
||||||
<Shapes size={16} />
|
<Shapes size={16} />
|
||||||
<div className="text-xs font-medium">Manage widgets</div>
|
<div className="text-xs font-medium">Manage widgets</div>
|
||||||
</button> */}
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import { FC, useEffect, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// computed
|
// computed
|
||||||
import { useHome } from "@/hooks/store/use-home";
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
|
import { EWidgetKeys, WidgetLoader } from "../loaders";
|
||||||
import { AddLink } from "./action";
|
import { AddLink } from "./action";
|
||||||
import { ProjectLinkDetail } from "./link-detail";
|
import { ProjectLinkDetail } from "./link-detail";
|
||||||
import { TLinkOperations } from "./use-links";
|
import { TLinkOperations } from "./use-links";
|
||||||
import { EWidgetKeys, WidgetLoader } from "../loaders";
|
|
||||||
|
|
||||||
export type TLinkOperationsModal = Exclude<TLinkOperations, "create">;
|
export type TLinkOperationsModal = Exclude<TLinkOperations, "create">;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { THomeWidgetProps } from "@plane/types";
|
||||||
import { useHome } from "@/hooks/store/use-home";
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
import { LinkCreateUpdateModal } from "./create-update-link-modal";
|
import { LinkCreateUpdateModal } from "./create-update-link-modal";
|
||||||
import { ProjectLinkList } from "./links";
|
import { ProjectLinkList } from "./links";
|
||||||
import { useLinks } from "./use-links";
|
import { useLinks } from "./use-links";
|
||||||
import { THomeWidgetProps } from "@plane/types";
|
|
||||||
|
|
||||||
export const DashboardQuickLinks = observer((props: THomeWidgetProps) => {
|
export const DashboardQuickLinks = observer((props: THomeWidgetProps) => {
|
||||||
const { workspaceSlug } = props;
|
const { workspaceSlug } = props;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// components
|
// components
|
||||||
import { RecentActivityWidgetLoader } from "./recent-activity";
|
|
||||||
import { QuickLinksWidgetLoader } from "./quick-links";
|
import { QuickLinksWidgetLoader } from "./quick-links";
|
||||||
|
import { RecentActivityWidgetLoader } from "./recent-activity";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,38 +14,45 @@ import { attachInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree
|
||||||
|
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane helpers
|
// plane helpers
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
// ui
|
// ui
|
||||||
import { InstructionType } from "@plane/types";
|
import { InstructionType, TWidgetEntityData } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
import { DropIndicator, ToggleSwitch } from "@plane/ui";
|
import { DropIndicator, ToggleSwitch } from "@plane/ui";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
import { WidgetItemDragHandle } from "./widget-item-drag-handle";
|
import { WidgetItemDragHandle } from "./widget-item-drag-handle";
|
||||||
import { getCanDrop, getInstructionFromPayload } from "./widget.helpers";
|
import { getCanDrop, getInstructionFromPayload } from "./widget.helpers";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
widgetId: string;
|
||||||
isLastChild: boolean;
|
isLastChild: boolean;
|
||||||
widget: any;
|
|
||||||
handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void;
|
handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void;
|
||||||
|
handleToggle: (workspaceSlug: string, widgetKey: string, is_enabled: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WidgetItem: FC<Props> = observer((props) => {
|
export const WidgetItem: FC<Props> = observer((props) => {
|
||||||
// props
|
// props
|
||||||
const { isLastChild, widget, handleDrop } = props;
|
const { widgetId, isLastChild, handleDrop, handleToggle } = props;
|
||||||
|
const { workspaceSlug } = useParams();
|
||||||
//state
|
//state
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);
|
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);
|
||||||
|
|
||||||
//ref
|
//ref
|
||||||
const elementRef = useRef<HTMLDivElement>(null);
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
|
// hooks
|
||||||
|
const { widgetsMap } = useHome();
|
||||||
|
// derived values
|
||||||
|
const widget = widgetsMap[widgetId] as TWidgetEntityData;
|
||||||
|
|
||||||
// drag and drop
|
// drag and drop
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const element = elementRef.current;
|
const element = elementRef.current;
|
||||||
|
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
const initialData = { id: widget.id, isGroup: false };
|
const initialData = { id: widget.key, isGroup: false };
|
||||||
return combine(
|
return combine(
|
||||||
draggable({
|
draggable({
|
||||||
element,
|
element,
|
||||||
|
|
@ -62,7 +69,7 @@ export const WidgetItem: FC<Props> = observer((props) => {
|
||||||
getOffset: pointerOutsideOfPreview({ x: "0px", y: "0px" }),
|
getOffset: pointerOutsideOfPreview({ x: "0px", y: "0px" }),
|
||||||
render: ({ container }) => {
|
render: ({ container }) => {
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
root.render(<div className="rounded bg-custom-background-100 text-sm p-1 pr-2">{widget.title}</div>);
|
root.render(<div className="rounded bg-custom-background-100 text-sm p-1 pr-2">{widget.key}</div>);
|
||||||
return () => root.unmount();
|
return () => root.unmount();
|
||||||
},
|
},
|
||||||
nativeSetDragImage,
|
nativeSetDragImage,
|
||||||
|
|
@ -104,7 +111,7 @@ export const WidgetItem: FC<Props> = observer((props) => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [elementRef?.current, isDragging, isLastChild, widget.id]);
|
}, [elementRef?.current, isDragging, isLastChild, widget.key]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<div className="">
|
||||||
|
|
@ -120,9 +127,12 @@ export const WidgetItem: FC<Props> = observer((props) => {
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<WidgetItemDragHandle sort_order={widget.sort_order} isDragging={isDragging} />
|
<WidgetItemDragHandle sort_order={widget.sort_order} isDragging={isDragging} />
|
||||||
<div>{widget.title}</div>
|
<div>{widget.key.replaceAll("_", " ")}</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <ToggleSwitch /> */}
|
<ToggleSwitch
|
||||||
|
value={widget.is_enabled}
|
||||||
|
onChange={() => handleToggle(workspaceSlug.toString(), widget.key, !widget.is_enabled)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{isLastChild && <DropIndicator isVisible={instruction === "reorder-below"} />}
|
{isLastChild && <DropIndicator isVisible={instruction === "reorder-below"} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,14 @@ import {
|
||||||
DropTargetRecord,
|
DropTargetRecord,
|
||||||
ElementDragPayload,
|
ElementDragPayload,
|
||||||
} from "@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types";
|
} from "@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { setToast, TOAST_TYPE } from "@plane/ui";
|
||||||
import { useHome } from "@/hooks/store/use-home";
|
import { useHome } from "@/hooks/store/use-home";
|
||||||
import { WidgetItem } from "./widget-item";
|
import { WidgetItem } from "./widget-item";
|
||||||
import { getInstructionFromPayload, TargetData } from "./widget.helpers";
|
import { getInstructionFromPayload, TargetData } from "./widget.helpers";
|
||||||
|
|
||||||
const WIDGETS_LIST = [
|
export const WidgetList = observer(({ workspaceSlug }: { workspaceSlug: string }) => {
|
||||||
{ id: 1, title: "quick links" },
|
const { orderedWidgets, reorderWidget, toggleWidget } = useHome();
|
||||||
{ id: 2, title: "recents" },
|
|
||||||
{ id: 3, title: "stickies" },
|
|
||||||
];
|
|
||||||
export const WidgetList = ({ workspaceSlug }: { workspaceSlug: string }) => {
|
|
||||||
const { reorderWidget } = useHome();
|
|
||||||
|
|
||||||
const handleDrop = (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => {
|
const handleDrop = (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => {
|
||||||
const dropTargets = location?.current?.dropTargets ?? [];
|
const dropTargets = location?.current?.dropTargets ?? [];
|
||||||
|
|
@ -30,20 +27,34 @@ export const WidgetList = ({ workspaceSlug }: { workspaceSlug: string }) => {
|
||||||
|
|
||||||
if (!sourceData.id) return;
|
if (!sourceData.id) return;
|
||||||
if (droppedId) {
|
if (droppedId) {
|
||||||
reorderWidget(workspaceSlug, sourceData.id, droppedId, instruction); /** sequence */
|
try {
|
||||||
|
reorderWidget(workspaceSlug, sourceData.id, droppedId, instruction); /** sequence */
|
||||||
|
setToast({
|
||||||
|
type: TOAST_TYPE.SUCCESS,
|
||||||
|
title: "Success!",
|
||||||
|
message: "Widget reordered successfully.",
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
setToast({
|
||||||
|
type: TOAST_TYPE.ERROR,
|
||||||
|
title: "Error!",
|
||||||
|
message: "Error occurred while reordering widget.",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-4">
|
<div className="my-4">
|
||||||
{WIDGETS_LIST.map((widget, index) => (
|
{orderedWidgets.map((widget, index) => (
|
||||||
<WidgetItem
|
<WidgetItem
|
||||||
key={widget.id}
|
key={widget}
|
||||||
widget={widget}
|
widgetId={widget}
|
||||||
isLastChild={index === WIDGETS_LIST.length - 1}
|
isLastChild={index === orderedWidgets.length - 1}
|
||||||
handleDrop={handleDrop}
|
handleDrop={handleDrop}
|
||||||
|
handleToggle={toggleWidget}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
import { extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
|
||||||
import { IFavorite, InstructionType, IPragmaticPayloadLocation, TDropTarget } from "@plane/types";
|
import { InstructionType, IPragmaticPayloadLocation, TDropTarget, TWidgetEntityData } from "@plane/types";
|
||||||
|
|
||||||
export type TargetData = {
|
export type TargetData = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -11,7 +11,7 @@ export type TargetData = {
|
||||||
/**
|
/**
|
||||||
* extracts the Payload and translates the instruction for the current dropTarget based on drag and drop payload
|
* extracts the Payload and translates the instruction for the current dropTarget based on drag and drop payload
|
||||||
* @param dropTarget dropTarget for which the instruction is required
|
* @param dropTarget dropTarget for which the instruction is required
|
||||||
* @param source the dragging favorite data that is being dragged on the dropTarget
|
* @param source the dragging widget data that is being dragged on the dropTarget
|
||||||
* @param location location includes the data of all the dropTargets the source is being dragged on
|
* @param location location includes the data of all the dropTargets the source is being dragged on
|
||||||
* @returns Instruction for dropTarget
|
* @returns Instruction for dropTarget
|
||||||
*/
|
*/
|
||||||
|
|
@ -37,7 +37,7 @@ export const getInstructionFromPayload = (
|
||||||
instruction = dropTargetData.isChild ? "reorder-above" : "make-child";
|
instruction = dropTargetData.isChild ? "reorder-above" : "make-child";
|
||||||
}
|
}
|
||||||
|
|
||||||
// if source that is being dragged is a group. A group cannon be a child of any other favorite,
|
// if source that is being dragged is a group. A group cannon be a child of any other widget,
|
||||||
// hence if current instruction is to be a child of dropTarget then reorder-above instead
|
// hence if current instruction is to be a child of dropTarget then reorder-above instead
|
||||||
if (instruction === "make-child" && sourceData.isGroup) instruction = "reorder-above";
|
if (instruction === "make-child" && sourceData.isGroup) instruction = "reorder-above";
|
||||||
|
|
||||||
|
|
@ -45,18 +45,18 @@ export const getInstructionFromPayload = (
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provides a boolean to indicate if the favorite can be dropped onto the droptarget
|
* This provides a boolean to indicate if the widget can be dropped onto the droptarget
|
||||||
* @param source
|
* @param source
|
||||||
* @param favorite
|
* @param widget
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getCanDrop = (source: TDropTarget, favorite: IFavorite | undefined) => {
|
export const getCanDrop = (source: TDropTarget, widget: TWidgetEntityData | undefined) => {
|
||||||
const sourceData = source?.data;
|
const sourceData = source?.data;
|
||||||
|
|
||||||
if (!sourceData) return false;
|
if (!sourceData) return false;
|
||||||
|
|
||||||
// a favorite cannot be dropped on to itself
|
// a widget cannot be dropped on to itself
|
||||||
if (sourceData.id === favorite?.id) return false;
|
if (sourceData.id === widget?.key) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
|
import { TRecentActivityFilterKeys } from "@plane/types";
|
||||||
import { CustomMenu } from "@plane/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
import { cn } from "@plane/utils";
|
import { cn } from "@plane/utils";
|
||||||
import { TRecentActivityFilterKeys } from "@plane/types";
|
|
||||||
|
|
||||||
export type TFiltersDropdown = {
|
export type TFiltersDropdown = {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,18 @@
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// types
|
// types
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { Briefcase, FileText } from "lucide-react";
|
||||||
import { TActivityEntityData, THomeWidgetProps, TRecentActivityFilterKeys } from "@plane/types";
|
import { TActivityEntityData, THomeWidgetProps, TRecentActivityFilterKeys } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
|
import { LayersIcon } from "@plane/ui";
|
||||||
|
import { WorkspaceService } from "@/plane-web/services";
|
||||||
|
import { EmptyWorkspace } from "../empty-states";
|
||||||
|
import { EWidgetKeys, WidgetLoader } from "../loaders";
|
||||||
import { FiltersDropdown } from "./filters";
|
import { FiltersDropdown } from "./filters";
|
||||||
import { RecentIssue } from "./issue";
|
import { RecentIssue } from "./issue";
|
||||||
import { WorkspaceService } from "@/plane-web/services";
|
|
||||||
import useSWR from "swr";
|
|
||||||
import { RecentProject } from "./project";
|
|
||||||
import { RecentPage } from "./page";
|
import { RecentPage } from "./page";
|
||||||
import { EWidgetKeys, WidgetLoader } from "../loaders";
|
import { RecentProject } from "./project";
|
||||||
import { Briefcase, FileText } from "lucide-react";
|
|
||||||
import { LayersIcon } from "@plane/ui";
|
|
||||||
import { EmptyWorkspace } from "../empty-states";
|
|
||||||
|
|
||||||
const WIDGET_KEY = EWidgetKeys.RECENT_ACTIVITY;
|
const WIDGET_KEY = EWidgetKeys.RECENT_ACTIVITY;
|
||||||
const workspaceService = new WorkspaceService();
|
const workspaceService = new WorkspaceService();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { FileText } from "lucide-react";
|
||||||
import { TActivityEntityData, TPageEntityData } from "@plane/types";
|
import { TActivityEntityData, TPageEntityData } from "@plane/types";
|
||||||
import { Avatar, Logo } from "@plane/ui";
|
import { Avatar, Logo } from "@plane/ui";
|
||||||
|
import { getFileURL } from "@plane/utils";
|
||||||
import { ListItem } from "@/components/core/list";
|
import { ListItem } from "@/components/core/list";
|
||||||
import { calculateTimeAgo } from "@/helpers/date-time.helper";
|
import { calculateTimeAgo } from "@/helpers/date-time.helper";
|
||||||
import { FileText } from "lucide-react";
|
|
||||||
import { getFileURL } from "@plane/utils";
|
|
||||||
import { useMember } from "@/hooks/store";
|
import { useMember } from "@/hooks/store";
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
|
|
||||||
type BlockProps = {
|
type BlockProps = {
|
||||||
activity: TActivityEntityData;
|
activity: TActivityEntityData;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import { TActivityEntityData, TProjectEntityData } from "@plane/types";
|
import { TActivityEntityData, TProjectEntityData } from "@plane/types";
|
||||||
import { Logo } from "@plane/ui";
|
import { Logo } from "@plane/ui";
|
||||||
import { ListItem } from "@/components/core/list";
|
import { ListItem } from "@/components/core/list";
|
||||||
import { calculateTimeAgo } from "@/helpers/date-time.helper";
|
|
||||||
import { MemberDropdown } from "@/components/dropdowns";
|
import { MemberDropdown } from "@/components/dropdowns";
|
||||||
import { useRouter } from "next/navigation";
|
import { calculateTimeAgo } from "@/helpers/date-time.helper";
|
||||||
|
|
||||||
type BlockProps = {
|
type BlockProps = {
|
||||||
activity: TActivityEntityData;
|
activity: TActivityEntityData;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
import { TWidgetEntityData } from "@plane/types";
|
import { THomeWidgetKeys, TWidgetEntityData } from "@plane/types";
|
||||||
import { WorkspaceService } from "@/plane-web/services";
|
import { WorkspaceService } from "@/plane-web/services";
|
||||||
import { IWorkspaceLinkStore, WorkspaceLinkStore } from "./link.store";
|
import { IWorkspaceLinkStore, WorkspaceLinkStore } from "./link.store";
|
||||||
|
|
||||||
|
|
@ -9,6 +9,9 @@ export interface IHomeStore {
|
||||||
// observables
|
// observables
|
||||||
showWidgetSettings: boolean;
|
showWidgetSettings: boolean;
|
||||||
widgetsMap: Record<string, TWidgetEntityData>;
|
widgetsMap: Record<string, TWidgetEntityData>;
|
||||||
|
widgets: THomeWidgetKeys[];
|
||||||
|
// computed
|
||||||
|
orderedWidgets: THomeWidgetKeys[];
|
||||||
//stores
|
//stores
|
||||||
quickLinks: IWorkspaceLinkStore;
|
quickLinks: IWorkspaceLinkStore;
|
||||||
// actions
|
// actions
|
||||||
|
|
@ -22,7 +25,7 @@ export class HomeStore implements IHomeStore {
|
||||||
// observables
|
// observables
|
||||||
showWidgetSettings = false;
|
showWidgetSettings = false;
|
||||||
widgetsMap: Record<string, TWidgetEntityData> = {};
|
widgetsMap: Record<string, TWidgetEntityData> = {};
|
||||||
widgets: string[] = [];
|
widgets: THomeWidgetKeys[] = [];
|
||||||
// stores
|
// stores
|
||||||
quickLinks: IWorkspaceLinkStore;
|
quickLinks: IWorkspaceLinkStore;
|
||||||
// services
|
// services
|
||||||
|
|
@ -34,6 +37,8 @@ export class HomeStore implements IHomeStore {
|
||||||
showWidgetSettings: observable,
|
showWidgetSettings: observable,
|
||||||
widgetsMap: observable,
|
widgetsMap: observable,
|
||||||
widgets: observable,
|
widgets: observable,
|
||||||
|
// computed
|
||||||
|
orderedWidgets: computed,
|
||||||
// actions
|
// actions
|
||||||
toggleWidgetSettings: action,
|
toggleWidgetSettings: action,
|
||||||
fetchWidgets: action,
|
fetchWidgets: action,
|
||||||
|
|
@ -47,6 +52,10 @@ export class HomeStore implements IHomeStore {
|
||||||
this.quickLinks = new WorkspaceLinkStore();
|
this.quickLinks = new WorkspaceLinkStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get orderedWidgets() {
|
||||||
|
return orderBy(Object.values(this.widgetsMap), "sort_order", "desc").map((widget) => widget.key);
|
||||||
|
}
|
||||||
|
|
||||||
toggleWidgetSettings = (value?: boolean) => {
|
toggleWidgetSettings = (value?: boolean) => {
|
||||||
this.showWidgetSettings = value !== undefined ? value : !this.showWidgetSettings;
|
this.showWidgetSettings = value !== undefined ? value : !this.showWidgetSettings;
|
||||||
};
|
};
|
||||||
|
|
@ -55,7 +64,7 @@ export class HomeStore implements IHomeStore {
|
||||||
try {
|
try {
|
||||||
const widgets = await this.workspaceService.fetchWorkspaceWidgets(workspaceSlug);
|
const widgets = await this.workspaceService.fetchWorkspaceWidgets(workspaceSlug);
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.widgets = widgets.map((widget) => widget.key);
|
this.widgets = orderBy(Object.values(widgets), "sort_order", "desc").map((widget) => widget.key);
|
||||||
widgets.forEach((widget) => {
|
widgets.forEach((widget) => {
|
||||||
this.widgetsMap[widget.key] = widget;
|
this.widgetsMap[widget.key] = widget;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1
web/ee/components/home/header.tsx
Normal file
1
web/ee/components/home/header.tsx
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const HomePageHeader = () => <></>;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue