Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
15ff3ee
wip
gakshita Dec 19, 2024
ae89bed
chore: wip
gakshita Dec 24, 2024
8b43497
Merge branch 'preview' of https://github.com/makeplane/plane into fea…
gakshita Dec 31, 2024
1c020f0
Merge branch 'preview' of https://github.com/makeplane/plane into fea…
gakshita Dec 31, 2024
c2412bb
fix: preserved old component
gakshita Jan 3, 2025
2254d50
fix
gakshita Jan 3, 2025
a031765
fix: seperate route added
gakshita Jan 3, 2025
0d564e6
fix
gakshita Jan 3, 2025
0263021
Merge branch 'preview' of https://github.com/makeplane/plane into fea…
gakshita Jan 3, 2025
bba8436
Only return user ID of project members
sangeethailango Jan 3, 2025
99d9702
Return issue ID
sangeethailango Jan 3, 2025
ff29170
fix: recents api integrations
gakshita Jan 3, 2025
fb9c364
fix: types
gakshita Jan 3, 2025
5a1c521
fix: types
gakshita Jan 3, 2025
314f714
fix: added tooltips
gakshita Jan 6, 2025
afa330e
chore: added apis
gakshita Jan 6, 2025
94a9b00
fix: widgets fix
gakshita Jan 6, 2025
92607d2
fix: lint
gakshita Jan 6, 2025
cbfb4ec
Merge branch 'preview' of https://github.com/makeplane/plane into fea…
gakshita Jan 6, 2025
e9cd538
fix: integrated dashboard apis
gakshita Jan 6, 2025
062d2fe
fix: added ee dummy component for header
gakshita Jan 6, 2025
8ae0503
Merge branch 'feat-home-integrations' of https://github.com/makeplane…
gakshita Jan 6, 2025
4bea812
fix: removed logs
gakshita Jan 7, 2025
de02303
Merge branch 'preview' of https://github.com/makeplane/plane into fix…
gakshita Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/types/src/home.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TLogoProps } from "./common";
import { TIssuePriorities } from "./issues";

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";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure consistent naming for the recent activity widget.

Here, you define "recents" as part of THomeWidgetKeys. However, in loader.tsx, the enum uses "recent_activity". Please unify the naming (e.g., use all "recents" or all "recent_activity") to avoid potential type mismatches.


export type THomeWidgetProps = {
workspaceSlug: string;
Expand Down Expand Up @@ -69,7 +69,7 @@ export type TLinkIdMap = {
};

export type TWidgetEntityData = {
key: string;
key: THomeWidgetKeys;
name: string;
is_enabled: boolean;
sort_order: number;
Expand Down
18 changes: 11 additions & 7 deletions web/core/components/home/home-dashboard-widgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ import { DashboardQuickLinks } from "./widgets/links";
import { ManageWidgetsModal } from "./widgets/manage";

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 },
recent_activity: { component: RecentActivityWidget, fullWidth: false },
stickies: { component: StickiesWidget, fullWidth: false },
recents: { component: RecentActivityWidget, fullWidth: false },
my_stickies: { component: StickiesWidget, fullWidth: false },
new_at_plane: { component: null, fullWidth: false },
quick_tutorial: { component: null, fullWidth: false },
Comment on lines +15 to +21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Confirm unified widget keys usage.

Defining "recents", "my_stickies", etc., is consistent with the updated THomeWidgetKeys. Nonetheless, be sure to align any usage of "recent_activity" in the codebase (e.g., EWidgetKeys.RECENT_ACTIVITY) with "recents" to prevent runtime errors.

};

export const DashboardWidgets = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const { toggleWidgetSettings, showWidgetSettings } = useHome();
const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets } = useHome();

if (!workspaceSlug) return null;

Expand All @@ -36,9 +38,11 @@ export const DashboardWidgets = observer(() => {
handleOnClose={() => toggleWidgetSettings(false)}
/>

{Object.entries(WIDGETS_LIST).map(([key, widget]) => {
const WidgetComponent = widget.component;
if (widget.fullWidth)
{orderedWidgets.map((key) => {
const WidgetComponent = WIDGETS_LIST[key]?.component;
const isEnabled = widgetsMap[key]?.is_enabled;
if (!WidgetComponent || !isEnabled) return null;
if (WIDGETS_LIST[key]?.fullWidth)
return (
<div key={key} className="lg:col-span-2">
<WidgetComponent workspaceSlug={workspaceSlug.toString()} />
Expand Down
27 changes: 14 additions & 13 deletions web/core/components/home/root.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import useSWR from "swr";
import { ContentWrapper } from "@plane/ui";
import { EmptyState } from "@/components/empty-state";
import { TourRoot } from "@/components/onboarding";
Expand All @@ -11,7 +11,7 @@ import { PRODUCT_TOUR_COMPLETED } from "@/constants/event-tracker";
// helpers
import { cn } from "@/helpers/common.helper";
// 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 useSize from "@/hooks/use-window-size";
import { IssuePeekOverview } from "../issues";
Expand All @@ -29,12 +29,20 @@ export const WorkspaceHomeView = observer(() => {
const { data: currentUser } = useUser();
const { data: currentUserProfile, updateTourCompleted } = useUserProfile();
const { captureEvent } = useEventTracker();
const { homeDashboardId, fetchHomeDashboardWidgets } = useDashboard();
const { toggleWidgetSettings } = useHome();
const { toggleWidgetSettings, fetchWidgets } = useHome();
const { joinedProjectIds, loader } = useProject();

const [windowWidth] = useSize();

useSWR(
workspaceSlug ? `HOME_DASHBOARD_WIDGETS_${workspaceSlug}` : null,
workspaceSlug ? () => fetchWidgets(workspaceSlug?.toString()) : null,
{
revalidateIfStale: true,
revalidateOnFocus: false,
revalidateOnReconnect: true,
}
);

const handleTourCompleted = () => {
updateTourCompleted()
.then(() => {
Expand All @@ -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
return (
<>
Expand All @@ -63,7 +64,7 @@ export const WorkspaceHomeView = observer(() => {
<TourRoot onComplete={handleTourCompleted} />
</div>
)}
{homeDashboardId && joinedProjectIds && (
{joinedProjectIds && (
<>
{joinedProjectIds.length > 0 || loader ? (
<>
Expand Down
4 changes: 2 additions & 2 deletions web/core/components/home/user-greetings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ export const UserGreetingsView: FC<IUserGreetingsView> = (props) => {
</div>
</h6>
</div>
{/* <button
<button
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"
>
<Shapes size={16} />
<div className="text-xs font-medium">Manage widgets</div>
</button> */}
</button>
</div>
);
};
2 changes: 1 addition & 1 deletion web/core/components/home/widgets/links/links.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { FC, useEffect, useState } from "react";
import { observer } from "mobx-react";
// computed
import { useHome } from "@/hooks/store/use-home";
import { EWidgetKeys, WidgetLoader } from "../loaders";
import { AddLink } from "./action";
import { ProjectLinkDetail } from "./link-detail";
import { TLinkOperations } from "./use-links";
import { EWidgetKeys, WidgetLoader } from "../loaders";

export type TLinkOperationsModal = Exclude<TLinkOperations, "create">;

Expand Down
2 changes: 1 addition & 1 deletion web/core/components/home/widgets/links/root.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { observer } from "mobx-react";
import useSWR from "swr";
import { THomeWidgetProps } from "@plane/types";
import { useHome } from "@/hooks/store/use-home";
import { LinkCreateUpdateModal } from "./create-update-link-modal";
import { ProjectLinkList } from "./links";
import { useLinks } from "./use-links";
import { THomeWidgetProps } from "@plane/types";

export const DashboardQuickLinks = observer((props: THomeWidgetProps) => {
const { workspaceSlug } = props;
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/home/widgets/loaders/loader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// components
import { RecentActivityWidgetLoader } from "./recent-activity";
import { QuickLinksWidgetLoader } from "./quick-links";
import { RecentActivityWidgetLoader } from "./recent-activity";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential mismatch between widget keys.

You are importing RecentActivityWidgetLoader and mapping it to EWidgetKeys.RECENT_ACTIVITY = "recent_activity", but in other files, the key "recents" is used. This mismatch may cause rendering or typing issues. Please ensure the widget key is consistently named across all files.


// types

Expand Down
28 changes: 19 additions & 9 deletions web/core/components/home/widgets/manage/widget-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,45 @@ import { attachInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree

import { observer } from "mobx-react";
// plane helpers
import { useParams } from "next/navigation";
import { createRoot } from "react-dom/client";
// ui
import { InstructionType } from "@plane/types";
import { InstructionType, TWidgetEntityData } from "@plane/types";
// components
import { DropIndicator, ToggleSwitch } from "@plane/ui";
// helpers
import { cn } from "@plane/utils";
import { useHome } from "@/hooks/store/use-home";
import { WidgetItemDragHandle } from "./widget-item-drag-handle";
import { getCanDrop, getInstructionFromPayload } from "./widget.helpers";

type Props = {
widgetId: string;
isLastChild: boolean;
widget: any;
handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void;
handleToggle: (workspaceSlug: string, widgetKey: string, is_enabled: boolean) => void;
};

export const WidgetItem: FC<Props> = observer((props) => {
// props
const { isLastChild, widget, handleDrop } = props;
const { widgetId, isLastChild, handleDrop, handleToggle } = props;
const { workspaceSlug } = useParams();
//state
const [isDragging, setIsDragging] = useState(false);
const [instruction, setInstruction] = useState<InstructionType | undefined>(undefined);

//ref
const elementRef = useRef<HTMLDivElement>(null);
// hooks
const { widgetsMap } = useHome();
// derived values
const widget = widgetsMap[widgetId] as TWidgetEntityData;

// drag and drop
useEffect(() => {
const element = elementRef.current;

if (!element) return;
const initialData = { id: widget.id, isGroup: false };
const initialData = { id: widget.key, isGroup: false };
return combine(
draggable({
element,
Expand All @@ -62,7 +69,7 @@ export const WidgetItem: FC<Props> = observer((props) => {
getOffset: pointerOutsideOfPreview({ x: "0px", y: "0px" }),
render: ({ 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();
},
nativeSetDragImage,
Expand Down Expand Up @@ -104,7 +111,7 @@ export const WidgetItem: FC<Props> = observer((props) => {
})
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [elementRef?.current, isDragging, isLastChild, widget.id]);
}, [elementRef?.current, isDragging, isLastChild, widget.key]);

return (
<div className="">
Expand All @@ -120,9 +127,12 @@ export const WidgetItem: FC<Props> = observer((props) => {
>
<div className="flex items-center">
<WidgetItemDragHandle sort_order={widget.sort_order} isDragging={isDragging} />
<div>{widget.title}</div>
<div>{widget.key.replaceAll("_", " ")}</div>
</div>
{/* <ToggleSwitch /> */}
<ToggleSwitch
value={widget.is_enabled}
onChange={() => handleToggle(workspaceSlug.toString(), widget.key, !widget.is_enabled)}
/>
</div>
{isLastChild && <DropIndicator isVisible={instruction === "reorder-below"} />}
</div>
Expand Down
37 changes: 24 additions & 13 deletions web/core/components/home/widgets/manage/widget-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@ import {
DropTargetRecord,
ElementDragPayload,
} 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 { WidgetItem } from "./widget-item";
import { getInstructionFromPayload, TargetData } from "./widget.helpers";

const WIDGETS_LIST = [
{ id: 1, title: "quick links" },
{ id: 2, title: "recents" },
{ id: 3, title: "stickies" },
];
export const WidgetList = ({ workspaceSlug }: { workspaceSlug: string }) => {
const { reorderWidget } = useHome();
export const WidgetList = observer(({ workspaceSlug }: { workspaceSlug: string }) => {
const { orderedWidgets, reorderWidget, toggleWidget } = useHome();

const handleDrop = (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => {
const dropTargets = location?.current?.dropTargets ?? [];
Expand All @@ -30,20 +27,34 @@ export const WidgetList = ({ workspaceSlug }: { workspaceSlug: string }) => {

if (!sourceData.id) return;
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 (
<div className="my-4">
{WIDGETS_LIST.map((widget, index) => (
{orderedWidgets.map((widget, index) => (
<WidgetItem
key={widget.id}
widget={widget}
isLastChild={index === WIDGETS_LIST.length - 1}
key={widget}
widgetId={widget}
isLastChild={index === orderedWidgets.length - 1}
handleDrop={handleDrop}
handleToggle={toggleWidget}
/>
))}
</div>
);
};
});
16 changes: 8 additions & 8 deletions web/core/components/home/widgets/manage/widget.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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 = {
id: string;
Expand All @@ -11,7 +11,7 @@ export type TargetData = {
/**
* 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 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
* @returns Instruction for dropTarget
*/
Expand All @@ -37,26 +37,26 @@ export const getInstructionFromPayload = (
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
if (instruction === "make-child" && sourceData.isGroup) instruction = "reorder-above";

return instruction;
};

/**
* 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 favorite
* @param widget
* @returns
*/
export const getCanDrop = (source: TDropTarget, favorite: IFavorite | undefined) => {
export const getCanDrop = (source: TDropTarget, widget: TWidgetEntityData | undefined) => {
const sourceData = source?.data;

if (!sourceData) return false;

// a favorite cannot be dropped on to itself
if (sourceData.id === favorite?.id) return false;
// a widget cannot be dropped on to itself
if (sourceData.id === widget?.key) return false;

return true;
};
2 changes: 1 addition & 1 deletion web/core/components/home/widgets/recents/filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import { FC } from "react";
import { observer } from "mobx-react";
import { ChevronDown } from "lucide-react";
import { TRecentActivityFilterKeys } from "@plane/types";
import { CustomMenu } from "@plane/ui";
import { cn } from "@plane/utils";
import { TRecentActivityFilterKeys } from "@plane/types";

export type TFiltersDropdown = {
className?: string;
Expand Down
Loading
Loading