diff --git a/web/core/components/dashboard/index.ts b/web/core/components/dashboard/index.ts deleted file mode 100644 index e88b1c701d9..00000000000 --- a/web/core/components/dashboard/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./widgets"; -export * from "./project-empty-state"; diff --git a/web/core/components/dashboard/project-empty-state.tsx b/web/core/components/dashboard/project-empty-state.tsx deleted file mode 100644 index eb06d21be00..00000000000 --- a/web/core/components/dashboard/project-empty-state.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { observer } from "mobx-react"; -import Image from "next/image"; -// ui -import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; -import { Button } from "@plane/ui"; -// hooks -import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store"; -// assets -import ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp"; - -export const DashboardProjectEmptyState = observer(() => { - // store hooks - const { toggleCreateProjectModal } = useCommandPalette(); - const { setTrackElement } = useEventTracker(); - const { allowPermissions } = useUserPermissions(); - - // derived values - const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE); - - return ( -
-

Overview of your projects, activity, and metrics

-

- Welcome to Plane, we are excited to have you here. Create your first project and track your work items, and this - page will transform into a space that helps you progress. Admins will also see items which help their team - progress. -

- Project empty state - {canCreateProject && ( -
- -
- )} -
- ); -}); diff --git a/web/core/components/dashboard/widgets/assigned-issues.tsx b/web/core/components/dashboard/widgets/assigned-issues.tsx deleted file mode 100644 index cc2b74314e1..00000000000 --- a/web/core/components/dashboard/widgets/assigned-issues.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { useEffect, useState } from "react"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import { Tab } from "@headlessui/react"; -import { EDurationFilters, FILTERED_ISSUES_TABS_LIST, UNFILTERED_ISSUES_TABS_LIST } from "@plane/constants"; -import { TAssignedIssuesWidgetFilters, TAssignedIssuesWidgetResponse } from "@plane/types"; -// hooks -import { Card } from "@plane/ui"; -import { - DurationFilterDropdown, - IssuesErrorState, - TabsList, - WidgetIssuesList, - WidgetLoader, - WidgetProps, -} from "@/components/dashboard/widgets"; -import { getCustomDates, getRedirectionFilters, getTabKey } from "@/helpers/dashboard.helper"; -import { useDashboard } from "@/hooks/store"; -// components -// helpers -// types -// constants - -const WIDGET_KEY = "assigned_issues"; - -export const AssignedIssuesWidget: React.FC = observer((props) => { - const { dashboardId, workspaceSlug } = props; - // states - const [fetching, setFetching] = useState(false); - // store hooks - const { fetchWidgetStats, getWidgetDetails, getWidgetStats, getWidgetStatsError, updateDashboardWidgetFilters } = - useDashboard(); - // derived values - const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); - const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); - const widgetStatsError = getWidgetStatsError(workspaceSlug, dashboardId, WIDGET_KEY); - const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE; - const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab); - const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? []; - - const handleUpdateFilters = async (filters: Partial) => { - if (!widgetDetails) return; - - setFetching(true); - - await updateDashboardWidgetFilters(workspaceSlug, dashboardId, widgetDetails.id, { - widgetKey: WIDGET_KEY, - filters, - }); - - const filterDates = getCustomDates( - filters.duration ?? selectedDurationFilter, - filters.custom_dates ?? selectedCustomDates - ); - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - issue_type: filters.tab ?? selectedTab, - ...(filterDates.trim() !== "" ? { target_date: filterDates } : {}), - expand: "issue_relation", - }).finally(() => setFetching(false)); - }; - - useEffect(() => { - const filterDates = getCustomDates(selectedDurationFilter, selectedCustomDates); - - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - issue_type: selectedTab, - ...(filterDates.trim() !== "" ? { target_date: filterDates } : {}), - expand: "issue_relation", - }); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const filterParams = getRedirectionFilters(selectedTab); - const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST; - const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab); - - if ((!widgetDetails || !widgetStats) && !widgetStatsError) return ; - - return ( - - {widgetStatsError ? ( - - handleUpdateFilters({ - duration: EDurationFilters.NONE, - tab: "pending", - }) - } - /> - ) : ( - widgetStats && ( - <> -
- - Assigned to you - - { - if (val === "custom" && customDates) { - handleUpdateFilters({ - duration: val, - custom_dates: customDates, - }); - return; - } - - if (val === selectedDurationFilter) return; - - let newTab = selectedTab; - // switch to pending tab if target date is changed to none - if (val === "none" && selectedTab !== "completed") newTab = "pending"; - // switch to upcoming tab if target date is changed to other than none - if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed") - newTab = "upcoming"; - - handleUpdateFilters({ - duration: val, - tab: newTab, - }); - }} - /> -
- { - const newSelectedTab = tabsList[i]; - handleUpdateFilters({ tab: newSelectedTab?.key ?? "completed" }); - }} - className="h-full flex flex-col" - > - - - {tabsList.map((tab) => { - if (tab.key !== selectedTab) return null; - - return ( - - - - ); - })} - - - - ) - )} -
- ); -}); diff --git a/web/core/components/dashboard/widgets/created-issues.tsx b/web/core/components/dashboard/widgets/created-issues.tsx deleted file mode 100644 index 47dc6c268eb..00000000000 --- a/web/core/components/dashboard/widgets/created-issues.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { useEffect, useState } from "react"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import { Tab } from "@headlessui/react"; -import { EDurationFilters, FILTERED_ISSUES_TABS_LIST, UNFILTERED_ISSUES_TABS_LIST } from "@plane/constants"; -import { TCreatedIssuesWidgetFilters, TCreatedIssuesWidgetResponse } from "@plane/types"; -// hooks -import { Card } from "@plane/ui"; -import { - DurationFilterDropdown, - IssuesErrorState, - TabsList, - WidgetIssuesList, - WidgetLoader, - WidgetProps, -} from "@/components/dashboard/widgets"; -import { getCustomDates, getRedirectionFilters, getTabKey } from "@/helpers/dashboard.helper"; -import { useDashboard } from "@/hooks/store"; -// components -// helpers -// types -// constants - -const WIDGET_KEY = "created_issues"; - -export const CreatedIssuesWidget: React.FC = observer((props) => { - const { dashboardId, workspaceSlug } = props; - // states - const [fetching, setFetching] = useState(false); - // store hooks - const { fetchWidgetStats, getWidgetDetails, getWidgetStats, getWidgetStatsError, updateDashboardWidgetFilters } = - useDashboard(); - // derived values - const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); - const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); - const widgetStatsError = getWidgetStatsError(workspaceSlug, dashboardId, WIDGET_KEY); - const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE; - const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab); - const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? []; - - const handleUpdateFilters = async (filters: Partial) => { - if (!widgetDetails) return; - - setFetching(true); - - await updateDashboardWidgetFilters(workspaceSlug, dashboardId, widgetDetails.id, { - widgetKey: WIDGET_KEY, - filters, - }); - - const filterDates = getCustomDates( - filters.duration ?? selectedDurationFilter, - filters.custom_dates ?? selectedCustomDates - ); - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - issue_type: filters.tab ?? selectedTab, - ...(filterDates.trim() !== "" ? { target_date: filterDates } : {}), - }).finally(() => setFetching(false)); - }; - - useEffect(() => { - const filterDates = getCustomDates(selectedDurationFilter, selectedCustomDates); - - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - issue_type: selectedTab, - ...(filterDates.trim() !== "" ? { target_date: filterDates } : {}), - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const filterParams = getRedirectionFilters(selectedTab); - const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST; - const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab); - - if ((!widgetDetails || !widgetStats) && !widgetStatsError) return ; - - return ( - - {widgetStatsError ? ( - - handleUpdateFilters({ - duration: EDurationFilters.NONE, - tab: "pending", - }) - } - /> - ) : ( - widgetStats && ( - <> -
- - Created by you - - { - if (val === "custom" && customDates) { - handleUpdateFilters({ - duration: val, - custom_dates: customDates, - }); - return; - } - - if (val === selectedDurationFilter) return; - - let newTab = selectedTab; - // switch to pending tab if target date is changed to none - if (val === "none" && selectedTab !== "completed") newTab = "pending"; - // switch to upcoming tab if target date is changed to other than none - if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed") - newTab = "upcoming"; - - handleUpdateFilters({ - duration: val, - tab: newTab, - }); - }} - /> -
- { - const newSelectedTab = tabsList[i]; - handleUpdateFilters({ tab: newSelectedTab.key ?? "completed" }); - }} - className="h-full flex flex-col" - > - - - {tabsList.map((tab) => { - if (tab.key !== selectedTab) return null; - - return ( - - - - ); - })} - - - - ) - )} -
- ); -}); diff --git a/web/core/components/dashboard/widgets/dropdowns/duration-filter.tsx b/web/core/components/dashboard/widgets/dropdowns/duration-filter.tsx deleted file mode 100644 index 2a897de82a9..00000000000 --- a/web/core/components/dashboard/widgets/dropdowns/duration-filter.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { ChevronDown } from "lucide-react"; -// components -import { DURATION_FILTER_OPTIONS, EDurationFilters } from "@plane/constants"; -import { CustomMenu } from "@plane/ui"; -import { DateFilterModal } from "@/components/core"; -// ui -// helpers -import { getDurationFilterDropdownLabel } from "@/helpers/dashboard.helper"; -// constants - -type Props = { - customDates?: string[]; - onChange: (value: EDurationFilters, customDates?: string[]) => void; - value: EDurationFilters; -}; - -export const DurationFilterDropdown: React.FC = (props) => { - const { customDates, onChange, value } = props; - // states - const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false); - - return ( - <> - setIsDateFilterModalOpen(false)} - onSelect={(val) => onChange(EDurationFilters.CUSTOM, val)} - title="Due date" - /> - - {getDurationFilterDropdownLabel(value, customDates ?? [])} - - - } - placement="bottom-end" - closeOnSelect - > - {DURATION_FILTER_OPTIONS.map((option) => ( - { - if (option.key === "custom") setIsDateFilterModalOpen(true); - else onChange(option.key); - }} - > - {option.label} - - ))} - - - ); -}; diff --git a/web/core/components/dashboard/widgets/dropdowns/index.ts b/web/core/components/dashboard/widgets/dropdowns/index.ts deleted file mode 100644 index cff4cdb449c..00000000000 --- a/web/core/components/dashboard/widgets/dropdowns/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./duration-filter"; diff --git a/web/core/components/dashboard/widgets/empty-states/assigned-issues.tsx b/web/core/components/dashboard/widgets/empty-states/assigned-issues.tsx deleted file mode 100644 index 169547846e4..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/assigned-issues.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -import { TIssuesListTypes } from "@plane/types"; -import CompletedIssuesDark from "@/public/empty-state/dashboard/dark/completed-issues.svg"; -import OverdueIssuesDark from "@/public/empty-state/dashboard/dark/overdue-issues.svg"; -import UpcomingIssuesDark from "@/public/empty-state/dashboard/dark/upcoming-issues.svg"; -import CompletedIssuesLight from "@/public/empty-state/dashboard/light/completed-issues.svg"; -import OverdueIssuesLight from "@/public/empty-state/dashboard/light/overdue-issues.svg"; -import UpcomingIssuesLight from "@/public/empty-state/dashboard/light/upcoming-issues.svg"; - -export const ASSIGNED_ISSUES_EMPTY_STATES = { - pending: { - title: "Work items assigned to you that are pending\nwill show up here.", - darkImage: UpcomingIssuesDark, - lightImage: UpcomingIssuesLight, - }, - upcoming: { - title: "Upcoming work items assigned to\nyou will show up here.", - darkImage: UpcomingIssuesDark, - lightImage: UpcomingIssuesLight, - }, - overdue: { - title: "Work items assigned to you that are past\ntheir due date will show up here.", - darkImage: OverdueIssuesDark, - lightImage: OverdueIssuesLight, - }, - completed: { - title: "Work items assigned to you that you have\nmarked Completed will show up here.", - darkImage: CompletedIssuesDark, - lightImage: CompletedIssuesLight, - }, -}; -type Props = { - type: TIssuesListTypes; -}; - -export const AssignedIssuesEmptyState: React.FC = (props) => { - const { type } = props; - // next-themes - const { resolvedTheme } = useTheme(); - - const typeDetails = ASSIGNED_ISSUES_EMPTY_STATES[type]; - - const image = resolvedTheme === "dark" ? typeDetails.darkImage : typeDetails.lightImage; - - // TODO: update empty state logic to use a general component - return ( -
-
- Assigned work items -
-

{typeDetails.title}

-
- ); -}; diff --git a/web/core/components/dashboard/widgets/empty-states/created-issues.tsx b/web/core/components/dashboard/widgets/empty-states/created-issues.tsx deleted file mode 100644 index 49a96f1fb3b..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/created-issues.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -import { TIssuesListTypes } from "@plane/types"; -import CompletedIssuesDark from "@/public/empty-state/dashboard/dark/completed-issues.svg"; -import OverdueIssuesDark from "@/public/empty-state/dashboard/dark/overdue-issues.svg"; -import UpcomingIssuesDark from "@/public/empty-state/dashboard/dark/upcoming-issues.svg"; -import CompletedIssuesLight from "@/public/empty-state/dashboard/light/completed-issues.svg"; -import OverdueIssuesLight from "@/public/empty-state/dashboard/light/overdue-issues.svg"; -import UpcomingIssuesLight from "@/public/empty-state/dashboard/light/upcoming-issues.svg"; - -export const CREATED_ISSUES_EMPTY_STATES = { - pending: { - title: "Work items created by you that are pending\nwill show up here.", - darkImage: UpcomingIssuesDark, - lightImage: UpcomingIssuesLight, - }, - upcoming: { - title: "Upcoming work items you created\nwill show up here.", - darkImage: UpcomingIssuesDark, - lightImage: UpcomingIssuesLight, - }, - overdue: { - title: "Work items created by you that are past their\ndue date will show up here.", - darkImage: OverdueIssuesDark, - lightImage: OverdueIssuesLight, - }, - completed: { - title: "Work items created by you that you have\nmarked completed will show up here.", - darkImage: CompletedIssuesDark, - lightImage: CompletedIssuesLight, - }, -}; - -type Props = { - type: TIssuesListTypes; -}; - -export const CreatedIssuesEmptyState: React.FC = (props) => { - const { type } = props; - // next-themes - const { resolvedTheme } = useTheme(); - - const typeDetails = CREATED_ISSUES_EMPTY_STATES[type]; - - const image = resolvedTheme === "dark" ? typeDetails.darkImage : typeDetails.lightImage; - - return ( -
-
- Assigned work items -
-

{typeDetails.title}

-
- ); -}; diff --git a/web/core/components/dashboard/widgets/empty-states/index.ts b/web/core/components/dashboard/widgets/empty-states/index.ts deleted file mode 100644 index 72ca1dbb2dc..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./assigned-issues"; -export * from "./created-issues"; -export * from "./issues-by-priority"; -export * from "./issues-by-state-group"; -export * from "./recent-activity"; -export * from "./recent-collaborators"; diff --git a/web/core/components/dashboard/widgets/empty-states/issues-by-priority.tsx b/web/core/components/dashboard/widgets/empty-states/issues-by-priority.tsx deleted file mode 100644 index 7f70d3bf303..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/issues-by-priority.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -// assets -import DarkImage from "@/public/empty-state/dashboard/dark/issues-by-priority.svg"; -import LightImage from "@/public/empty-state/dashboard/light/issues-by-priority.svg"; - -export const IssuesByPriorityEmptyState = () => { - // next-themes - const { resolvedTheme } = useTheme(); - - const image = resolvedTheme === "dark" ? DarkImage : LightImage; - - return ( -
-
- Work items by state group -
-

- Work items assigned to you, broken down by -
- priority will show up here. -

-
- ); -}; diff --git a/web/core/components/dashboard/widgets/empty-states/issues-by-state-group.tsx b/web/core/components/dashboard/widgets/empty-states/issues-by-state-group.tsx deleted file mode 100644 index 69d4761e5ec..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/issues-by-state-group.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -// assets -import DarkImage from "@/public/empty-state/dashboard/dark/issues-by-state-group.svg"; -import LightImage from "@/public/empty-state/dashboard/light/issues-by-state-group.svg"; - -export const IssuesByStateGroupEmptyState = () => { - // next-themes - const { resolvedTheme } = useTheme(); - - const image = resolvedTheme === "dark" ? DarkImage : LightImage; - - return ( -
-
- Work items by state group -
-

- Work items assigned to you, broken down by state, -
- will show up here. -

-
- ); -}; diff --git a/web/core/components/dashboard/widgets/empty-states/recent-activity.tsx b/web/core/components/dashboard/widgets/empty-states/recent-activity.tsx deleted file mode 100644 index f25f3992088..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/recent-activity.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -// assets -import DarkImage from "@/public/empty-state/dashboard/dark/recent-activity.svg"; -import LightImage from "@/public/empty-state/dashboard/light/recent-activity.svg"; - -export const RecentActivityEmptyState = () => { - // next-themes - const { resolvedTheme } = useTheme(); - - const image = resolvedTheme === "dark" ? DarkImage : LightImage; - - return ( -
-
- Work items by state group -
-

- All your work items activities across -
- projects will show up here. -

-
- ); -}; diff --git a/web/core/components/dashboard/widgets/empty-states/recent-collaborators.tsx b/web/core/components/dashboard/widgets/empty-states/recent-collaborators.tsx deleted file mode 100644 index d1a1200aa59..00000000000 --- a/web/core/components/dashboard/widgets/empty-states/recent-collaborators.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import Image from "next/image"; -import { useTheme } from "next-themes"; -// assets -import DarkImage1 from "@/public/empty-state/dashboard/dark/recent-collaborators-1.svg"; -import DarkImage2 from "@/public/empty-state/dashboard/dark/recent-collaborators-2.svg"; -import DarkImage3 from "@/public/empty-state/dashboard/dark/recent-collaborators-3.svg"; -import LightImage1 from "@/public/empty-state/dashboard/light/recent-collaborators-1.svg"; -import LightImage2 from "@/public/empty-state/dashboard/light/recent-collaborators-2.svg"; -import LightImage3 from "@/public/empty-state/dashboard/light/recent-collaborators-3.svg"; - -export const RecentCollaboratorsEmptyState = () => { - // next-themes - const { resolvedTheme } = useTheme(); - - const image1 = resolvedTheme === "dark" ? DarkImage1 : LightImage1; - const image2 = resolvedTheme === "dark" ? DarkImage2 : LightImage2; - const image3 = resolvedTheme === "dark" ? DarkImage3 : LightImage3; - - return ( -
-

- Compare your activities with the top -
- seven in your project. -

-
-
- Recent collaborators -
-
- Recent collaborators -
-
- Recent collaborators -
-
-
- ); -}; diff --git a/web/core/components/dashboard/widgets/error-states/index.ts b/web/core/components/dashboard/widgets/error-states/index.ts deleted file mode 100644 index bd8854f3744..00000000000 --- a/web/core/components/dashboard/widgets/error-states/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./issues"; diff --git a/web/core/components/dashboard/widgets/error-states/issues.tsx b/web/core/components/dashboard/widgets/error-states/issues.tsx deleted file mode 100644 index fb3e3550b44..00000000000 --- a/web/core/components/dashboard/widgets/error-states/issues.tsx +++ /dev/null @@ -1,34 +0,0 @@ -"use client"; - -import { AlertTriangle, RefreshCcw } from "lucide-react"; -// ui -import { Button } from "@plane/ui"; - -type Props = { - isRefreshing: boolean; - onClick: () => void; -}; - -export const IssuesErrorState: React.FC = (props) => { - const { isRefreshing, onClick } = props; - - return ( -
-
-
- -
-

There was an error in fetching widget details

- -
-
- ); -}; diff --git a/web/core/components/dashboard/widgets/index.ts b/web/core/components/dashboard/widgets/index.ts deleted file mode 100644 index e622d708f71..00000000000 --- a/web/core/components/dashboard/widgets/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from "./dropdowns"; -export * from "./empty-states"; -export * from "./error-states"; -export * from "./issue-panels"; -export * from "./loaders"; -export * from "./assigned-issues"; -export * from "./created-issues"; -export * from "./overview-stats"; -export * from "./recent-activity"; -export * from "./recent-collaborators"; -export * from "./recent-projects"; diff --git a/web/core/components/dashboard/widgets/issue-panels/index.ts b/web/core/components/dashboard/widgets/issue-panels/index.ts deleted file mode 100644 index f5b7d53d49e..00000000000 --- a/web/core/components/dashboard/widgets/issue-panels/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./issue-list-item"; -export * from "./issues-list"; -export * from "./tabs-list"; diff --git a/web/core/components/dashboard/widgets/issue-panels/issue-list-item.tsx b/web/core/components/dashboard/widgets/issue-panels/issue-list-item.tsx deleted file mode 100644 index 75471c6fd60..00000000000 --- a/web/core/components/dashboard/widgets/issue-panels/issue-list-item.tsx +++ /dev/null @@ -1,401 +0,0 @@ -"use client"; - -import { isToday } from "date-fns/isToday"; -import { observer } from "mobx-react"; -// types -import { TIssue, TWidgetIssue } from "@plane/types"; -// ui -import { Avatar, AvatarGroup, ControlLink, PriorityIcon } from "@plane/ui"; -// helpers -import { findTotalDaysInRange, getDate, renderFormattedDate } from "@/helpers/date-time.helper"; -import { getFileURL } from "@/helpers/file.helper"; -import { generateWorkItemLink } from "@/helpers/issue.helper"; -// hooks -import { useIssueDetail, useMember, useProject } from "@/hooks/store"; -// plane web components -import { IssueIdentifier } from "@/plane-web/components/issues"; - -export type IssueListItemProps = { - issueId: string; - onClick: (issue: TIssue) => void; - workspaceSlug: string; -}; - -export const AssignedUpcomingIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { getProjectById } = useProject(); - const { - issue: { getIssueById }, - } = useIssueDetail(); - // derived values - const issueDetails = getIssueById(issueId) as TWidgetIssue | undefined; - - if (!issueDetails || !issueDetails.project_id) return null; - - const projectDetails = getProjectById(issueDetails.project_id); - - const blockedByIssues = issueDetails.issue_relation?.filter((issue) => issue.relation_type === "blocked_by") ?? []; - - const blockedByIssueProjectDetails = - blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null; - - const targetDate = getDate(issueDetails.target_date); - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issueDetails?.project_id, - issueId: issueDetails?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issueDetails?.sequence_id, - }); - - return ( - onClick(issueDetails)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issueDetails.name}
-
-
- -
-
- {targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"} -
-
- {blockedByIssues.length > 0 - ? blockedByIssues.length > 1 - ? `${blockedByIssues.length} blockers` - : blockedByIssueProjectDetails && ( - - ) - : "-"} -
-
- ); -}); - -export const AssignedOverdueIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { getProjectById } = useProject(); - const { - issue: { getIssueById }, - } = useIssueDetail(); - // derived values - const issueDetails = getIssueById(issueId) as TWidgetIssue | undefined; - - if (!issueDetails || !issueDetails.project_id) return null; - - const projectDetails = getProjectById(issueDetails.project_id); - const blockedByIssues = issueDetails.issue_relation?.filter((issue) => issue.relation_type === "blocked_by") ?? []; - - const blockedByIssueProjectDetails = - blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null; - - const dueBy = findTotalDaysInRange(getDate(issueDetails.target_date), new Date(), false) ?? 0; - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issueDetails?.project_id, - issueId: issueDetails?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issueDetails?.sequence_id, - }); - - return ( - onClick(issueDetails)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issueDetails.name}
-
-
- -
-
- {dueBy} {`day${dueBy > 1 ? "s" : ""}`} -
-
- {blockedByIssues.length > 0 - ? blockedByIssues.length > 1 - ? `${blockedByIssues.length} blockers` - : blockedByIssueProjectDetails && ( - - ) - : "-"} -
-
- ); -}); - -export const AssignedCompletedIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { - issue: { getIssueById }, - } = useIssueDetail(); - const { getProjectById } = useProject(); - // derived values - const issueDetails = getIssueById(issueId); - - if (!issueDetails || !issueDetails.project_id) return null; - - const projectDetails = getProjectById(issueDetails.project_id); - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issueDetails?.project_id, - issueId: issueDetails?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issueDetails?.sequence_id, - }); - - return ( - onClick(issueDetails)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issueDetails.name}
-
-
- -
-
- ); -}); - -export const CreatedUpcomingIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { getUserDetails } = useMember(); - const { - issue: { getIssueById }, - } = useIssueDetail(); - const { getProjectById } = useProject(); - // derived values - const issue = getIssueById(issueId); - - if (!issue || !issue.project_id) return null; - - const projectDetails = getProjectById(issue.project_id); - const targetDate = getDate(issue.target_date); - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issue?.project_id, - issueId: issue?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issue?.sequence_id, - }); - - return ( - onClick(issue)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issue.name}
-
-
- -
-
- {targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"} -
-
- {issue.assignee_ids && issue.assignee_ids?.length > 0 ? ( - - {issue.assignee_ids?.map((assigneeId) => { - const userDetails = getUserDetails(assigneeId); - - if (!userDetails) return null; - - return ( - - ); - })} - - ) : ( - "-" - )} -
-
- ); -}); - -export const CreatedOverdueIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { getUserDetails } = useMember(); - const { - issue: { getIssueById }, - } = useIssueDetail(); - const { getProjectById } = useProject(); - // derived values - const issue = getIssueById(issueId); - - if (!issue || !issue.project_id) return null; - - const projectDetails = getProjectById(issue.project_id); - - const dueBy: number = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0; - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issue?.project_id, - issueId: issue?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issue?.sequence_id, - }); - - return ( - onClick(issue)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issue.name}
-
-
- -
-
- {dueBy} {`day${dueBy > 1 ? "s" : ""}`} -
-
- {issue.assignee_ids.length > 0 ? ( - - {issue.assignee_ids?.map((assigneeId) => { - const userDetails = getUserDetails(assigneeId); - - if (!userDetails) return null; - - return ( - - ); - })} - - ) : ( - "-" - )} -
-
- ); -}); - -export const CreatedCompletedIssueListItem: React.FC = observer((props) => { - const { issueId, onClick, workspaceSlug } = props; - // store hooks - const { getUserDetails } = useMember(); - const { - issue: { getIssueById }, - } = useIssueDetail(); - const { getProjectById } = useProject(); - // derived values - const issue = getIssueById(issueId); - - if (!issue || !issue.project_id) return null; - - const projectDetails = getProjectById(issue.project_id); - - const workItemLink = generateWorkItemLink({ - workspaceSlug, - projectId: issue?.project_id, - issueId: issue?.id, - projectIdentifier: projectDetails?.identifier, - sequenceId: issue?.sequence_id, - }); - - return ( - onClick(issue)} - className="grid grid-cols-12 gap-1 rounded px-3 py-2 hover:bg-custom-background-80" - > -
- {projectDetails && ( - - )} -
{issue.name}
-
-
- -
-
- {issue.assignee_ids.length > 0 ? ( - - {issue.assignee_ids?.map((assigneeId) => { - const userDetails = getUserDetails(assigneeId); - - if (!userDetails) return null; - - return ( - - ); - })} - - ) : ( - "-" - )} -
-
- ); -}); diff --git a/web/core/components/dashboard/widgets/issue-panels/issues-list.tsx b/web/core/components/dashboard/widgets/issue-panels/issues-list.tsx deleted file mode 100644 index 925e2ee9af8..00000000000 --- a/web/core/components/dashboard/widgets/issue-panels/issues-list.tsx +++ /dev/null @@ -1,134 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { TAssignedIssuesWidgetResponse, TCreatedIssuesWidgetResponse, TIssue, TIssuesListTypes } from "@plane/types"; -// hooks -// components -import { Loader, getButtonStyling } from "@plane/ui"; -import { - AssignedCompletedIssueListItem, - AssignedIssuesEmptyState, - AssignedOverdueIssueListItem, - AssignedUpcomingIssueListItem, - CreatedCompletedIssueListItem, - CreatedIssuesEmptyState, - CreatedOverdueIssueListItem, - CreatedUpcomingIssueListItem, - IssueListItemProps, -} from "@/components/dashboard/widgets"; -// ui -// helpers -import { cn } from "@/helpers/common.helper"; -import { getRedirectionFilters } from "@/helpers/dashboard.helper"; -// hooks -import useIssuePeekOverviewRedirection from "@/hooks/use-issue-peek-overview-redirection"; -import { usePlatformOS } from "@/hooks/use-platform-os"; - -export type WidgetIssuesListProps = { - isLoading: boolean; - tab: TIssuesListTypes; - type: "assigned" | "created"; - widgetStats: TAssignedIssuesWidgetResponse | TCreatedIssuesWidgetResponse; - workspaceSlug: string; -}; - -export const WidgetIssuesList: React.FC = (props) => { - const { isLoading, tab, type, widgetStats, workspaceSlug } = props; - // hooks - const { isMobile } = usePlatformOS(); - const { handleRedirection } = useIssuePeekOverviewRedirection(); - - // handlers - const handleIssuePeekOverview = (issue: TIssue) => handleRedirection(workspaceSlug, issue, isMobile); - - const filterParams = getRedirectionFilters(tab); - - const ISSUE_LIST_ITEM: { - [key: string]: { - [key in TIssuesListTypes]: React.FC; - }; - } = { - assigned: { - pending: AssignedUpcomingIssueListItem, - upcoming: AssignedUpcomingIssueListItem, - overdue: AssignedOverdueIssueListItem, - completed: AssignedCompletedIssueListItem, - }, - created: { - pending: CreatedUpcomingIssueListItem, - upcoming: CreatedUpcomingIssueListItem, - overdue: CreatedOverdueIssueListItem, - completed: CreatedCompletedIssueListItem, - }, - }; - - const issuesList = widgetStats.issues; - - return ( - <> -
- {isLoading ? ( - - - - - - - ) : issuesList.length > 0 ? ( - <> -
-
- Work items - - {widgetStats.count} - -
-
Priority
- {["upcoming", "pending"].includes(tab) &&
Due date
} - {tab === "overdue" &&
Due by
} - {type === "assigned" && tab !== "completed" &&
Blocked by
} - {type === "created" &&
Assigned to
} -
-
- {issuesList.map((issue) => { - const IssueListItem = ISSUE_LIST_ITEM[type][tab]; - - if (!IssueListItem) return null; - - return ( - - ); - })} -
- - ) : ( -
- {type === "assigned" && } - {type === "created" && } -
- )} -
- {!isLoading && issuesList.length > 0 && ( - - View all work items - - )} - - ); -}; diff --git a/web/core/components/dashboard/widgets/issue-panels/tabs-list.tsx b/web/core/components/dashboard/widgets/issue-panels/tabs-list.tsx deleted file mode 100644 index 9df044c37b7..00000000000 --- a/web/core/components/dashboard/widgets/issue-panels/tabs-list.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { observer } from "mobx-react"; -import { Tab } from "@headlessui/react"; -// helpers -import { EDurationFilters, FILTERED_ISSUES_TABS_LIST, UNFILTERED_ISSUES_TABS_LIST } from "@plane/constants"; -import { TIssuesListTypes } from "@plane/types"; -import { cn } from "@/helpers/common.helper"; -// types -// constants - -type Props = { - durationFilter: EDurationFilters; - selectedTab: TIssuesListTypes; -}; - -export const TabsList: React.FC = observer((props) => { - const { durationFilter, selectedTab } = props; - - const tabsList = durationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST; - const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab); - - return ( - -
- {tabsList.map((tab) => ( - - {tab.label} - - ))} - - ); -}); diff --git a/web/core/components/dashboard/widgets/loaders/assigned-issues.tsx b/web/core/components/dashboard/widgets/loaders/assigned-issues.tsx deleted file mode 100644 index 8a78fedf1a4..00000000000 --- a/web/core/components/dashboard/widgets/loaders/assigned-issues.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client"; - -// ui -import { Loader } from "@plane/ui"; - -export const AssignedIssuesWidgetLoader = () => ( - -
- - -
-
- - -
-
- - - - - -
-
-); diff --git a/web/core/components/dashboard/widgets/loaders/index.ts b/web/core/components/dashboard/widgets/loaders/index.ts deleted file mode 100644 index ee5286f0fbf..00000000000 --- a/web/core/components/dashboard/widgets/loaders/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./loader"; diff --git a/web/core/components/dashboard/widgets/loaders/issues-by-priority.tsx b/web/core/components/dashboard/widgets/loaders/issues-by-priority.tsx deleted file mode 100644 index c6f075b5806..00000000000 --- a/web/core/components/dashboard/widgets/loaders/issues-by-priority.tsx +++ /dev/null @@ -1,17 +0,0 @@ -"use client"; - -// ui -import { Loader } from "@plane/ui"; - -export const IssuesByPriorityWidgetLoader = () => ( - - -
- - - - - -
-
-); diff --git a/web/core/components/dashboard/widgets/loaders/issues-by-state-group.tsx b/web/core/components/dashboard/widgets/loaders/issues-by-state-group.tsx deleted file mode 100644 index 099323cce75..00000000000 --- a/web/core/components/dashboard/widgets/loaders/issues-by-state-group.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client"; - -import range from "lodash/range"; -// ui -import { Loader } from "@plane/ui"; - -export const IssuesByStateGroupWidgetLoader = () => ( - - -
-
-
- -
-
-
-
- {range(5).map((index) => ( - - ))} -
-
- -); diff --git a/web/core/components/dashboard/widgets/loaders/loader.tsx b/web/core/components/dashboard/widgets/loaders/loader.tsx deleted file mode 100644 index ae4038b38da..00000000000 --- a/web/core/components/dashboard/widgets/loaders/loader.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// components -import { TWidgetKeys } from "@plane/types"; -import { AssignedIssuesWidgetLoader } from "./assigned-issues"; -import { IssuesByPriorityWidgetLoader } from "./issues-by-priority"; -import { IssuesByStateGroupWidgetLoader } from "./issues-by-state-group"; -import { OverviewStatsWidgetLoader } from "./overview-stats"; -import { RecentActivityWidgetLoader } from "./recent-activity"; -import { RecentCollaboratorsWidgetLoader } from "./recent-collaborators"; -import { RecentProjectsWidgetLoader } from "./recent-projects"; -// types - -type Props = { - widgetKey: TWidgetKeys; -}; - -export const WidgetLoader: React.FC = (props) => { - const { widgetKey } = props; - - const loaders = { - overview_stats: , - assigned_issues: , - created_issues: , - issues_by_state_groups: , - issues_by_priority: , - recent_activity: , - recent_projects: , - recent_collaborators: , - }; - - return loaders[widgetKey]; -}; diff --git a/web/core/components/dashboard/widgets/loaders/overview-stats.tsx b/web/core/components/dashboard/widgets/loaders/overview-stats.tsx deleted file mode 100644 index e780bb39966..00000000000 --- a/web/core/components/dashboard/widgets/loaders/overview-stats.tsx +++ /dev/null @@ -1,16 +0,0 @@ -"use client"; - -import range from "lodash/range"; -// ui -import { Loader } from "@plane/ui"; - -export const OverviewStatsWidgetLoader = () => ( - - {range(4).map((index) => ( -
- - -
- ))} -
-); diff --git a/web/core/components/dashboard/widgets/loaders/recent-activity.tsx b/web/core/components/dashboard/widgets/loaders/recent-activity.tsx deleted file mode 100644 index 2df78a15af1..00000000000 --- a/web/core/components/dashboard/widgets/loaders/recent-activity.tsx +++ /dev/null @@ -1,22 +0,0 @@ -"use client"; - -import range from "lodash/range"; -// ui -import { Loader } from "@plane/ui"; - -export const RecentActivityWidgetLoader = () => ( - - - {range(7).map((index) => ( -
-
- -
-
- - -
-
- ))} -
-); diff --git a/web/core/components/dashboard/widgets/loaders/recent-collaborators.tsx b/web/core/components/dashboard/widgets/loaders/recent-collaborators.tsx deleted file mode 100644 index 2dceaf1320a..00000000000 --- a/web/core/components/dashboard/widgets/loaders/recent-collaborators.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import range from "lodash/range"; -// ui -import { Loader } from "@plane/ui"; - -export const RecentCollaboratorsWidgetLoader = () => ( - <> - {range(8).map((index) => ( - -
-
- -
- -
-
- ))} - -); diff --git a/web/core/components/dashboard/widgets/loaders/recent-projects.tsx b/web/core/components/dashboard/widgets/loaders/recent-projects.tsx deleted file mode 100644 index 38bc7e29a01..00000000000 --- a/web/core/components/dashboard/widgets/loaders/recent-projects.tsx +++ /dev/null @@ -1,22 +0,0 @@ -"use client"; - -import range from "lodash/range"; -// ui -import { Loader } from "@plane/ui"; - -export const RecentProjectsWidgetLoader = () => ( - - - {range(5).map((index) => ( -
-
- -
-
- - -
-
- ))} -
-); diff --git a/web/core/components/dashboard/widgets/overview-stats.tsx b/web/core/components/dashboard/widgets/overview-stats.tsx deleted file mode 100644 index 20421f33b49..00000000000 --- a/web/core/components/dashboard/widgets/overview-stats.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { useEffect } from "react"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import { TOverviewStatsWidgetResponse } from "@plane/types"; -// hooks -import { Card, ECardSpacing } from "@plane/ui"; -import { WidgetLoader } from "@/components/dashboard/widgets"; -import { cn } from "@/helpers/common.helper"; -import { renderFormattedPayloadDate } from "@/helpers/date-time.helper"; -import { useDashboard } from "@/hooks/store"; -// components -// helpers -// types - -export type WidgetProps = { - dashboardId: string; - workspaceSlug: string; -}; - -const WIDGET_KEY = "overview_stats"; - -export const OverviewStatsWidget: React.FC = observer((props) => { - const { dashboardId, workspaceSlug } = props; - // store hooks - const { fetchWidgetStats, getWidgetStats } = useDashboard(); - // derived values - const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); - - const today = renderFormattedPayloadDate(new Date()); - const STATS_LIST = [ - { - key: "assigned", - title: "Work items assigned", - count: widgetStats?.assigned_issues_count, - link: `/${workspaceSlug}/workspace-views/assigned`, - }, - { - key: "overdue", - title: "Work items overdue", - count: widgetStats?.pending_issues_count, - link: `/${workspaceSlug}/workspace-views/assigned/?state_group=backlog,unstarted,started&target_date=${today};before`, - }, - { - key: "created", - title: "Work items created", - count: widgetStats?.created_issues_count, - link: `/${workspaceSlug}/workspace-views/created`, - }, - { - key: "completed", - title: "Work items completed", - count: widgetStats?.completed_issues_count, - link: `/${workspaceSlug}/workspace-views/assigned?state_group=completed`, - }, - ]; - - useEffect(() => { - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (!widgetStats) return ; - - return ( - - {STATS_LIST.map((stat, index) => ( -
- -
-
-
{stat.count}
-

{stat.title}

-
-
- -
- ))} -
- ); -}); diff --git a/web/core/components/dashboard/widgets/recent-activity.tsx b/web/core/components/dashboard/widgets/recent-activity.tsx deleted file mode 100644 index 84ac985e10c..00000000000 --- a/web/core/components/dashboard/widgets/recent-activity.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import { History } from "lucide-react"; -// types -import { TRecentActivityWidgetResponse } from "@plane/types"; -// components -import { Card, Avatar, getButtonStyling } from "@plane/ui"; -import { ActivityIcon, ActivityMessage, IssueLink } from "@/components/core"; -import { RecentActivityEmptyState, WidgetLoader, WidgetProps } from "@/components/dashboard/widgets"; -// helpers -import { cn } from "@/helpers/common.helper"; -import { calculateTimeAgo } from "@/helpers/date-time.helper"; -import { getFileURL } from "@/helpers/file.helper"; -// hooks -import { useDashboard, useUser } from "@/hooks/store"; - -const WIDGET_KEY = "recent_activity"; - -export const RecentActivityWidget: React.FC = observer((props) => { - const { dashboardId, workspaceSlug } = props; - // store hooks - const { data: currentUser } = useUser(); - // derived values - const { fetchWidgetStats, getWidgetStats } = useDashboard(); - const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); - const redirectionLink = `/${workspaceSlug}/profile/${currentUser?.id}/activity`; - - useEffect(() => { - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (!widgetStats) return ; - - return ( - - - Your work item activities - - {widgetStats.length > 0 ? ( -
- {widgetStats.map((activity) => ( -
-
- {activity.field ? ( - activity.new_value === "restore" ? ( - - ) : ( -
- -
- ) - ) : activity.actor_detail.avatar_url && activity.actor_detail.avatar_url !== "" ? ( - - ) : ( -
- {activity.actor_detail.is_bot - ? activity.actor_detail.first_name.charAt(0) - : activity.actor_detail.display_name.charAt(0)} -
- )} -
-
-

- - {currentUser?.id === activity.actor_detail.id ? "You" : activity.actor_detail?.display_name}{" "} - - {activity.field ? ( - - ) : ( - - created - - )} -

-

- {calculateTimeAgo(activity.created_at)} -

-
-
- ))} - - View all - -
- ) : ( -
- -
- )} -
- ); -}); diff --git a/web/core/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx b/web/core/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx deleted file mode 100644 index 415809e0433..00000000000 --- a/web/core/components/dashboard/widgets/recent-collaborators/collaborators-list.tsx +++ /dev/null @@ -1,154 +0,0 @@ -"use client"; -import { useState } from "react"; -import sortBy from "lodash/sortBy"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import useSWR from "swr"; -// types -import { TRecentCollaboratorsWidgetResponse } from "@plane/types"; -// ui -import { Avatar } from "@plane/ui"; -// helpers -import { getFileURL } from "@/helpers/file.helper"; -// hooks -import { useDashboard, useMember, useUser } from "@/hooks/store"; -// components -import { WidgetLoader } from "../loaders"; - -type CollaboratorListItemProps = { - issueCount: number; - userId: string; - workspaceSlug: string; -}; - -const CollaboratorListItem: React.FC = observer((props) => { - const { issueCount, userId, workspaceSlug } = props; - // store hooks - const { data: currentUser } = useUser(); - const { getUserDetails } = useMember(); - // derived values - const userDetails = getUserDetails(userId); - const isCurrentUser = userId === currentUser?.id; - - if (!userDetails || userDetails.is_bot) return null; - - return ( - -
- -
-
- {isCurrentUser ? "You" : userDetails?.display_name} -
-

- {issueCount} active work items{issueCount > 1 ? "s" : ""} -

- - ); -}); - -type CollaboratorsListProps = { - dashboardId: string; - searchQuery?: string; - workspaceSlug: string; -}; - -const WIDGET_KEY = "recent_collaborators"; - -export const CollaboratorsList: React.FC = (props) => { - const { dashboardId, searchQuery = "", workspaceSlug } = props; - - // state - const [visibleItems, setVisibleItems] = useState(16); - const [isExpanded, setIsExpanded] = useState(false); - // store hooks - const { fetchWidgetStats } = useDashboard(); - const { getUserDetails } = useMember(); - const { data: currentUser } = useUser(); - - const { data: widgetStats } = useSWR( - workspaceSlug && dashboardId ? `WIDGET_STATS_${workspaceSlug}_${dashboardId}` : null, - workspaceSlug && dashboardId - ? () => - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - }) - : null - ) as { - data: TRecentCollaboratorsWidgetResponse[] | undefined; - }; - - if (!widgetStats) - return ( -
- -
- ); - - const sortedStats = sortBy(widgetStats, [(user) => user?.user_id !== currentUser?.id]); - - const filteredStats = sortedStats.filter((user) => { - if (!user) return false; - const userDetails = getUserDetails(user?.user_id); - if (!userDetails || userDetails.is_bot) return false; - const { display_name, first_name, last_name } = userDetails; - const searchLower = searchQuery.toLowerCase(); - return ( - display_name?.toLowerCase().includes(searchLower) || - first_name?.toLowerCase().includes(searchLower) || - last_name?.toLowerCase().includes(searchLower) - ); - }); - - // Update the displayedStats to always use the visibleItems limit - const handleLoadMore = () => { - setVisibleItems((prev) => { - const newValue = prev + 16; - if (newValue >= filteredStats.length) { - setIsExpanded(true); - return filteredStats.length; - } - return newValue; - }); - }; - - const handleHide = () => { - setVisibleItems(16); - setIsExpanded(false); - }; - - const displayedStats = filteredStats.slice(0, visibleItems); - - return ( - <> -
- {displayedStats?.map((user) => ( - - ))} -
- {filteredStats.length > visibleItems && !isExpanded && ( -
-
- Load more -
-
- )} - {isExpanded && ( -
-
Hide
-
- )} - - ); -}; diff --git a/web/core/components/dashboard/widgets/recent-collaborators/index.ts b/web/core/components/dashboard/widgets/recent-collaborators/index.ts deleted file mode 100644 index 1efe34c51ec..00000000000 --- a/web/core/components/dashboard/widgets/recent-collaborators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./root"; diff --git a/web/core/components/dashboard/widgets/recent-collaborators/root.tsx b/web/core/components/dashboard/widgets/recent-collaborators/root.tsx deleted file mode 100644 index 4d30642071b..00000000000 --- a/web/core/components/dashboard/widgets/recent-collaborators/root.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useState } from "react"; -import { Search } from "lucide-react"; -// types -import { Card } from "@plane/ui"; -import { WidgetProps } from "@/components/dashboard/widgets"; -// components -import { CollaboratorsList } from "./collaborators-list"; - -export const RecentCollaboratorsWidget: React.FC = (props) => { - const { dashboardId, workspaceSlug } = props; - // states - const [searchQuery, setSearchQuery] = useState(""); - - return ( - -
-
-

Collaborators

-

- View and find all members you collaborate with across projects -

-
-
- - setSearchQuery(e.target.value)} - /> -
-
- -
- ); -}; diff --git a/web/core/components/dashboard/widgets/recent-projects.tsx b/web/core/components/dashboard/widgets/recent-projects.tsx deleted file mode 100644 index 3e9c4e257b9..00000000000 --- a/web/core/components/dashboard/widgets/recent-projects.tsx +++ /dev/null @@ -1,134 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { observer } from "mobx-react"; -import Link from "next/link"; -import { Plus } from "lucide-react"; -// plane types -import { PROJECT_BACKGROUND_COLORS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; -import { TRecentProjectsWidgetResponse } from "@plane/types"; -// plane ui -import { Avatar, AvatarGroup, Card } from "@plane/ui"; -// components -import { Logo } from "@/components/common"; -import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets"; -// constants -// helpers -import { getFileURL } from "@/helpers/file.helper"; -// hooks -import { - useEventTracker, - useDashboard, - useProject, - useCommandPalette, - useUserPermissions, - useMember, -} from "@/hooks/store"; -// plane web constants - -const WIDGET_KEY = "recent_projects"; - -type ProjectListItemProps = { - projectId: string; - workspaceSlug: string; -}; - -const ProjectListItem: React.FC = observer((props) => { - const { projectId, workspaceSlug } = props; - // store hooks - const { getProjectById } = useProject(); - const { getUserDetails } = useMember(); - // derived values - const projectDetails = getProjectById(projectId); - - const randomBgColor = PROJECT_BACKGROUND_COLORS[Math.floor(Math.random() * PROJECT_BACKGROUND_COLORS.length)]; - - if (!projectDetails) return null; - - return ( - -
-
- -
-
-
-
- {projectDetails.name} -
-
- - {projectDetails.members?.map((memberId) => { - const userDetails = getUserDetails(memberId); - if (!userDetails) return null; - return ( - - ); - })} - -
-
- - ); -}); - -export const RecentProjectsWidget: React.FC = observer((props) => { - const { dashboardId, workspaceSlug } = props; - // store hooks - const { toggleCreateProjectModal } = useCommandPalette(); - const { setTrackElement } = useEventTracker(); - const { allowPermissions } = useUserPermissions(); - const { fetchWidgetStats, getWidgetStats } = useDashboard(); - // derived values - const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); - const canCreateProject = allowPermissions( - [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.WORKSPACE - ); - - useEffect(() => { - fetchWidgetStats(workspaceSlug, dashboardId, { - widget_key: WIDGET_KEY, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (!widgetStats) return ; - - return ( - - - Recent projects - -
- {canCreateProject && ( - - )} - {widgetStats.map((projectId) => ( - - ))} -
-
- ); -}); diff --git a/web/core/constants/fetch-keys.ts b/web/core/constants/fetch-keys.ts index d5bbf3ed65c..f0c9551a440 100644 --- a/web/core/constants/fetch-keys.ts +++ b/web/core/constants/fetch-keys.ts @@ -47,105 +47,19 @@ const paramsToKey = (params: any) => { return `${layoutKey}_${projectKey}_${stateGroupKey}_${stateKey}_${priorityKey}_${assigneesKey}_${mentionsKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}_${sub_issue}_${subscriberKey}`; }; -const myIssuesParamsToKey = (params: any) => { - const { assignees, created_by, labels, priority, state_group, subscriber, start_date, target_date } = params; - - let assigneesKey = assignees ? assignees.split(",") : []; - let createdByKey = created_by ? created_by.split(",") : []; - let stateGroupKey = state_group ? state_group.split(",") : []; - let subscriberKey = subscriber ? subscriber.split(",") : []; - let priorityKey = priority ? priority.split(",") : []; - let labelsKey = labels ? labels.split(",") : []; - const startDateKey = start_date ?? ""; - const targetDateKey = target_date ?? ""; - const type = params?.type ? params.type.toUpperCase() : "NULL"; - const groupBy = params?.group_by ? params.group_by.toUpperCase() : "NULL"; - const orderBy = params?.order_by ? params.order_by.toUpperCase() : "NULL"; - - // sorting each keys in ascending order - assigneesKey = assigneesKey.sort().join("_"); - createdByKey = createdByKey.sort().join("_"); - stateGroupKey = stateGroupKey.sort().join("_"); - subscriberKey = subscriberKey.sort().join("_"); - priorityKey = priorityKey.sort().join("_"); - labelsKey = labelsKey.sort().join("_"); - - return `${assigneesKey}_${createdByKey}_${stateGroupKey}_${subscriberKey}_${priorityKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}`; -}; - -export const CURRENT_USER = "CURRENT_USER"; -export const USER_WORKSPACE_INVITATIONS = "USER_WORKSPACE_INVITATIONS"; export const USER_WORKSPACES_LIST = "USER_WORKSPACES_LIST"; -export const WORKSPACE_DETAILS = (workspaceSlug: string) => `WORKSPACE_DETAILS_${workspaceSlug.toUpperCase()}`; - export const WORKSPACE_MEMBERS = (workspaceSlug: string) => `WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`; -export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) => `WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`; -export const WORKSPACE_INVITATIONS = (workspaceSlug: string) => `WORKSPACE_INVITATIONS_${workspaceSlug.toString()}`; -export const WORKSPACE_INVITATION = (invitationId: string) => `WORKSPACE_INVITATION_${invitationId}`; -export const LAST_ACTIVE_WORKSPACE_AND_PROJECTS = "LAST_ACTIVE_WORKSPACE_AND_PROJECTS"; -export const PROJECTS_LIST = ( - workspaceSlug: string, - params: { - is_favorite: "all" | boolean; - } -) => { - if (!params) return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}`; +export const WORKSPACE_INVITATION = (invitationId: string) => `WORKSPACE_INVITATION_${invitationId}`; - return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite.toString().toUpperCase()}`; -}; export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`; export const PROJECT_MEMBERS = (projectId: string) => `PROJECT_MEMBERS_${projectId.toUpperCase()}`; -export const PROJECT_INVITATIONS = (projectId: string) => `PROJECT_INVITATIONS_${projectId.toString()}`; - -export const PROJECT_ISSUES_LIST = (workspaceSlug: string, projectId: string) => - `PROJECT_ISSUES_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}`; -export const PROJECT_ISSUES_LIST_WITH_PARAMS = (projectId: string, params?: any) => { - if (!params) return `PROJECT_ISSUES_LIST_WITH_PARAMS_${projectId.toUpperCase()}`; - - const paramsKey = paramsToKey(params); - return `PROJECT_ISSUES_LIST_WITH_PARAMS_${projectId.toUpperCase()}_${paramsKey}`; -}; -export const PROJECT_ARCHIVED_ISSUES_LIST_WITH_PARAMS = (projectId: string, params?: any) => { - if (!params) return `PROJECT_ARCHIVED_ISSUES_LIST_WITH_PARAMS_${projectId.toUpperCase()}`; - - const paramsKey = paramsToKey(params); - - return `PROJECT_ARCHIVED_ISSUES_LIST_WITH_PARAMS_${projectId.toUpperCase()}_${paramsKey}`; -}; - -export const PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS = (projectId: string, params?: any) => { - if (!params) return `PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS${projectId.toUpperCase()}`; - - const paramsKey = paramsToKey(params); - - return `PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS${projectId.toUpperCase()}_${paramsKey}`; -}; - -export const GLOBAL_VIEWS_LIST = (workspaceSlug: string) => `GLOBAL_VIEWS_LIST_${workspaceSlug.toUpperCase()}`; -export const GLOBAL_VIEW_DETAILS = (globalViewId: string) => `GLOBAL_VIEW_DETAILS_${globalViewId.toUpperCase()}`; -export const GLOBAL_VIEW_ISSUES = (globalViewId: string) => `GLOBAL_VIEW_ISSUES_${globalViewId.toUpperCase()}`; - -export const PROJECT_ISSUES_DETAILS = (issueId: string) => `PROJECT_ISSUES_DETAILS_${issueId.toUpperCase()}`; -export const PROJECT_ISSUES_PROPERTIES = (projectId: string) => `PROJECT_ISSUES_PROPERTIES_${projectId.toUpperCase()}`; -export const PROJECT_ISSUES_COMMENTS = (issueId: string) => `PROJECT_ISSUES_COMMENTS_${issueId.toUpperCase()}`; -export const PROJECT_ISSUES_ACTIVITY = (issueId: string) => `PROJECT_ISSUES_ACTIVITY_${issueId.toUpperCase()}`; -export const PROJECT_ISSUE_BY_STATE = (projectId: string) => `PROJECT_ISSUE_BY_STATE_${projectId.toUpperCase()}`; -export const PROJECT_ISSUE_LABELS = (projectId: string) => `PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}`; -export const WORKSPACE_LABELS = (workspaceSlug: string) => `WORKSPACE_LABELS_${workspaceSlug.toUpperCase()}`; export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`; // cycles -export const CYCLES_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`; -export const INCOMPLETE_CYCLES_LIST = (projectId: string) => `INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`; -export const CURRENT_CYCLE_LIST = (projectId: string) => `CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`; -export const UPCOMING_CYCLES_LIST = (projectId: string) => `UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`; -export const DRAFT_CYCLES_LIST = (projectId: string) => `DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`; -export const COMPLETED_CYCLES_LIST = (projectId: string) => `COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`; -export const CYCLE_ISSUES = (cycleId: string) => `CYCLE_ISSUES_${cycleId.toUpperCase()}`; export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { if (!params) return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}`; @@ -153,47 +67,11 @@ export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}_${paramsKey.toUpperCase()}`; }; -export const CYCLE_DETAILS = (cycleId: string) => `CYCLE_DETAILS_${cycleId.toUpperCase()}`; -export const STATES_LIST = (projectId: string) => `STATES_LIST_${projectId.toUpperCase()}`; - -export const USER_ISSUE = (workspaceSlug: string) => `USER_ISSUE_${workspaceSlug.toUpperCase()}`; -export const USER_ISSUES = (workspaceSlug: string, params: any) => { - const paramsKey = myIssuesParamsToKey(params); - - return `USER_ISSUES_${workspaceSlug.toUpperCase()}_${paramsKey}`; -}; export const USER_ACTIVITY = (params: { cursor?: string }) => `USER_ACTIVITY_${params?.cursor}`; -export const USER_WORKSPACE_DASHBOARD = (workspaceSlug: string) => - `USER_WORKSPACE_DASHBOARD_${workspaceSlug.toUpperCase()}`; -export const USER_PROJECT_VIEW = (projectId: string) => `USER_PROJECT_VIEW_${projectId.toUpperCase()}`; - -export const MODULE_LIST = (projectId: string) => `MODULE_LIST_${projectId.toUpperCase()}`; -export const MODULE_ISSUES = (moduleId: string) => `MODULE_ISSUES_${moduleId.toUpperCase()}`; -export const MODULE_ISSUES_WITH_PARAMS = (moduleId: string, params?: any) => { - if (!params) return `MODULE_ISSUES_WITH_PARAMS_${moduleId.toUpperCase()}`; - - const paramsKey = paramsToKey(params); - - return `MODULE_ISSUES_WITH_PARAMS_${moduleId.toUpperCase()}_${paramsKey.toUpperCase()}`; -}; -export const MODULE_DETAILS = (moduleId: string) => `MODULE_DETAILS_${moduleId.toUpperCase()}`; - -export const VIEWS_LIST = (projectId: string) => `VIEWS_LIST_${projectId.toUpperCase()}`; -export const VIEW_DETAILS = (viewId: string) => `VIEW_DETAILS_${viewId.toUpperCase()}`; -export const VIEW_ISSUES = (viewId: string, params: any) => { - if (!params) return `VIEW_ISSUES_${viewId.toUpperCase()}`; - - const paramsKey = paramsToKey(params); - - return `VIEW_ISSUES_${viewId.toUpperCase()}_${paramsKey.toUpperCase()}`; -}; // Issues export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`; -export const SUB_ISSUES = (issueId: string) => `SUB_ISSUES_${issueId.toUpperCase()}`; -export const ISSUE_ATTACHMENTS = (issueId: string) => `ISSUE_ATTACHMENTS_${issueId.toUpperCase()}`; -export const ARCHIVED_ISSUE_DETAILS = (issueId: string) => `ARCHIVED_ISSUE_DETAILS_${issueId.toUpperCase()}`; // integrations export const APP_INTEGRATIONS = "APP_INTEGRATIONS"; @@ -222,21 +100,6 @@ export const GITHUB_REPOSITORY_INFO = (workspaceSlug: string, repoName: string) export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) => `SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`; -// Pages -export const RECENT_PAGES_LIST = (projectId: string) => `RECENT_PAGES_LIST_${projectId.toUpperCase()}`; -export const ALL_PAGES_LIST = (projectId: string) => `ALL_PAGES_LIST_${projectId.toUpperCase()}`; -export const ARCHIVED_PAGES_LIST = (projectId: string) => `ARCHIVED_PAGES_LIST_${projectId.toUpperCase}`; -export const FAVORITE_PAGES_LIST = (projectId: string) => `FAVORITE_PAGES_LIST_${projectId.toUpperCase()}`; -export const PRIVATE_PAGES_LIST = (projectId: string) => `PRIVATE_PAGES_LIST_${projectId.toUpperCase()}`; -export const SHARED_PAGES_LIST = (projectId: string) => `SHARED_PAGES_LIST_${projectId.toUpperCase()}`; -export const PAGE_DETAILS = (pageId: string) => `PAGE_DETAILS_${pageId.toUpperCase()}`; -export const PAGE_BLOCKS_LIST = (pageId: string) => `PAGE_BLOCK_LIST_${pageId.toUpperCase()}`; -export const PAGE_BLOCK_DETAILS = (pageId: string) => `PAGE_BLOCK_DETAILS_${pageId.toUpperCase()}`; -export const MY_PAGES_LIST = (pageId: string) => `MY_PAGE_LIST_${pageId}`; -// estimates -export const ESTIMATES_LIST = (projectId: string) => `ESTIMATES_LIST_${projectId.toUpperCase()}`; -export const ESTIMATE_DETAILS = (estimateId: string) => `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`; - // profile export const USER_PROFILE_DATA = (workspaceSlug: string, userId: string) => `USER_PROFILE_ACTIVITY_${workspaceSlug.toUpperCase()}_${userId.toUpperCase()}`; @@ -249,19 +112,6 @@ export const USER_PROFILE_ACTIVITY = ( ) => `USER_WORKSPACE_PROFILE_ACTIVITY_${workspaceSlug.toUpperCase()}_${userId.toUpperCase()}_${params?.cursor}`; export const USER_PROFILE_PROJECT_SEGREGATION = (workspaceSlug: string, userId: string) => `USER_PROFILE_PROJECT_SEGREGATION_${workspaceSlug.toUpperCase()}_${userId.toUpperCase()}`; -export const USER_PROFILE_ISSUES = (workspaceSlug: string, userId: string, params: any) => { - const paramsKey = myIssuesParamsToKey(params); - - return `USER_PROFILE_ISSUES_${workspaceSlug.toUpperCase()}_${userId.toUpperCase()}_${paramsKey}`; -}; - -// reactions -export const ISSUE_REACTION_LIST = (workspaceSlug: string, projectId: string, issueId: string) => - `ISSUE_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${issueId.toUpperCase()}`; -export const COMMENT_REACTION_LIST = (workspaceSlug: string, projectId: string, commendId: string) => - `COMMENT_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${commendId.toUpperCase()}`; // api-tokens export const API_TOKENS_LIST = (workspaceSlug: string) => `API_TOKENS_LIST_${workspaceSlug.toUpperCase()}`; -export const API_TOKEN_DETAILS = (workspaceSlug: string, tokenId: string) => - `API_TOKEN_DETAILS_${workspaceSlug.toUpperCase()}_${tokenId.toUpperCase()}`; diff --git a/web/core/hooks/use-comment-reaction.tsx b/web/core/hooks/use-comment-reaction.tsx deleted file mode 100644 index 848c9b42654..00000000000 --- a/web/core/hooks/use-comment-reaction.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import useSWR from "swr"; -// fetch keys -import { COMMENT_REACTION_LIST } from "@/constants/fetch-keys"; -// services -import { groupReactions } from "@/helpers/emoji.helper"; -import { useUser } from "@/hooks/store"; -import { IssueReactionService } from "@/services/issue"; -// helpers -// hooks -// services -const issueReactionService = new IssueReactionService(); - -const useCommentReaction: any = ( - workspaceSlug?: string | string[] | null, - projectId?: string | string[] | null, - commendId?: string | string[] | null -) => { - const { - data: commentReactions, - mutate: mutateCommentReactions, - error, - } = useSWR( - workspaceSlug && projectId && commendId - ? COMMENT_REACTION_LIST(workspaceSlug.toString(), projectId.toString(), commendId.toString()) - : null, - workspaceSlug && projectId && commendId - ? () => - issueReactionService.listIssueCommentReactions( - workspaceSlug.toString(), - projectId.toString(), - commendId.toString() - ) - : null - ); - - const { data: currentUser } = useUser(); - - const groupedReactions = groupReactions(commentReactions || [], "reaction"); - - /** - * @description Use this function to create user's reaction to an issue. This function will mutate the reactions state. - * @param {string} reaction - * @example handleReactionDelete("123") // 123 -> is emoji hexa-code - */ - - const handleReactionCreate = async (reaction: string) => { - if (!workspaceSlug || !projectId || !commendId) return; - - const data = await issueReactionService.createIssueCommentReaction( - workspaceSlug.toString(), - projectId.toString(), - commendId.toString(), - { reaction } - ); - - mutateCommentReactions((prev: any) => [...(prev || []), data]); - }; - - /** - * @description Use this function to delete user's reaction from an issue. This function will mutate the reactions state. - * @param {string} reaction - * @example handleReactionDelete("123") // 123 -> is emoji hexa-code - */ - - const handleReactionDelete = async (reaction: string) => { - if (!workspaceSlug || !projectId || !commendId) return; - - mutateCommentReactions( - (prevData: any) => prevData?.filter((r: any) => r.actor !== currentUser?.id || r.reaction !== reaction) || [], - false - ); - - await issueReactionService.deleteIssueCommentReaction( - workspaceSlug.toString(), - projectId.toString(), - commendId.toString(), - reaction - ); - - mutateCommentReactions(); - }; - - return { - isLoading: !commentReactions && !error, - commentReactions, - groupedReactions, - handleReactionCreate, - handleReactionDelete, - mutateCommentReactions, - } as const; -}; - -export default useCommentReaction; diff --git a/web/core/hooks/use-dynamic-dropdown.tsx b/web/core/hooks/use-dynamic-dropdown.tsx deleted file mode 100644 index 771763e3585..00000000000 --- a/web/core/hooks/use-dynamic-dropdown.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useCallback, useEffect } from "react"; -// plane helpers -import { useOutsideClickDetector } from "@plane/hooks"; - -/** - * Custom hook for dynamic dropdown position calculation. - * @param isOpen - Indicates whether the dropdown is open. - * @param handleClose - Callback to handle closing the dropdown. - * @param buttonRef - Ref object for the button triggering the dropdown. - * @param dropdownRef - Ref object for the dropdown element. - */ - -const useDynamicDropdownPosition = ( - isOpen: boolean, - handleClose: () => void, - buttonRef: React.RefObject, - dropdownRef: React.RefObject -) => { - const handlePosition = useCallback(() => { - const button = buttonRef.current; - const dropdown = dropdownRef.current; - - if (!dropdown || !button) return; - - const buttonRect = button.getBoundingClientRect(); - const dropdownRect = dropdown.getBoundingClientRect(); - - const { innerHeight, innerWidth, scrollX, scrollY } = window; - - let top: number = buttonRect.bottom + scrollY; - if (top + dropdownRect.height > innerHeight) top = innerHeight - dropdownRect.height; - - let left: number = buttonRect.left + scrollX + (buttonRect.width - dropdownRect.width) / 2; - if (left + dropdownRect.width > innerWidth) left = innerWidth - dropdownRect.width; - - dropdown.style.top = `${Math.max(top, 5)}px`; - dropdown.style.left = `${Math.max(left, 5)}px`; - }, [buttonRef, dropdownRef]); - - useEffect(() => { - if (isOpen) handlePosition(); - }, [handlePosition, isOpen]); - - useOutsideClickDetector(dropdownRef, () => { - if (isOpen) handleClose(); - }); - - const handleResize = useCallback(() => { - if (isOpen) { - handlePosition(); - } - }, [handlePosition, isOpen]); - - useEffect(() => { - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, [isOpen, handleResize]); -}; - -export default useDynamicDropdownPosition; diff --git a/web/core/hooks/use-url-hash.tsx b/web/core/hooks/use-url-hash.tsx deleted file mode 100644 index 9dc6d1770fa..00000000000 --- a/web/core/hooks/use-url-hash.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useEffect, useState } from "react"; - -const useURLHash = () => { - const [hashValue, setHashValue] = useState(); - - useEffect(() => { - const hash = window.location.hash?.split("#")[1]; - setHashValue(hash); - }, []); - - return hashValue; -}; - -export default useURLHash; diff --git a/web/core/lib/wrappers/crisp-wrapper.tsx b/web/core/lib/wrappers/crisp-wrapper.tsx deleted file mode 100644 index 1debf8ea5bd..00000000000 --- a/web/core/lib/wrappers/crisp-wrapper.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useEffect, ReactNode, FC } from "react"; -import { observer } from "mobx-react"; -// hooks -import { useUser } from "@/hooks/store"; - -declare global { - interface Window { - $crisp: unknown[]; - CRISP_WEBSITE_ID: unknown; - } -} - -export interface ICrispWrapper { - children: ReactNode; -} - -const CrispWrapper: FC = observer((props) => { - const { children } = props; - const { data: user } = useUser(); - - useEffect(() => { - if (typeof window && user?.email && process.env.NEXT_PUBLIC_CRISP_ID) { - window.$crisp = []; - window.CRISP_WEBSITE_ID = process.env.NEXT_PUBLIC_CRISP_ID; - (function () { - const d = document; - const s = d.createElement("script"); - s.src = "https://client.crisp.chat/l.js"; - s.async = true; - d.getElementsByTagName("head")[0].appendChild(s); - window.$crisp.push(["set", "user:email", [user.email]]); - window.$crisp.push(["do", "chat:hide"]); - window.$crisp.push(["do", "chat:close"]); - })(); - } - }, [user?.email]); - - return <>{children}; -}); - -export default CrispWrapper; diff --git a/web/helpers/string.helper.ts b/web/helpers/string.helper.ts index d0973109171..4c85e3ca84a 100644 --- a/web/helpers/string.helper.ts +++ b/web/helpers/string.helper.ts @@ -1,10 +1,4 @@ import DOMPurify from "isomorphic-dompurify"; -import { - CYCLE_ISSUES_WITH_PARAMS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - VIEW_ISSUES, -} from "@/constants/fetch-keys"; export const addSpaceIfCamelCase = (str: string) => { if (str === undefined || str === null) return ""; @@ -150,29 +144,6 @@ export const objToQueryParams = (obj: any) => { return params.toString(); }; -export const getFetchKeysForIssueMutation = (options: { - cycleId?: string | string[]; - moduleId?: string | string[]; - viewId?: string | string[]; - projectId: string; - viewGanttParams: any; - ganttParams: any; -}) => { - const { cycleId, moduleId, viewId, projectId, viewGanttParams, ganttParams } = options; - - const ganttFetchKey = cycleId - ? { ganttFetchKey: CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), ganttParams) } - : moduleId - ? { ganttFetchKey: MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), ganttParams) } - : viewId - ? { ganttFetchKey: VIEW_ISSUES(viewId.toString(), viewGanttParams) } - : { ganttFetchKey: PROJECT_ISSUES_LIST_WITH_PARAMS(projectId?.toString() ?? "", ganttParams) }; - - return { - ...ganttFetchKey, - }; -}; - /** * @returns {boolean} true if searchQuery is substring of text in the same order, false otherwise * @description Returns true if searchQuery is substring of text in the same order, false otherwise diff --git a/web/package.json b/web/package.json index 6312f9f903b..2122586e505 100644 --- a/web/package.json +++ b/web/package.json @@ -19,7 +19,6 @@ "@atlaskit/pragmatic-drag-and-drop": "^1.1.3", "@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^1.3.0", "@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.0.3", - "@blueprintjs/popover2": "^1.13.3", "@headlessui/react": "^1.7.3", "@intercom/messenger-js-sdk": "^0.0.12", "@plane/constants": "*", @@ -65,7 +64,6 @@ "smooth-scroll-into-view-if-needed": "^2.0.2", "swr": "^2.1.3", "tailwind-merge": "^2.0.0", - "use-debounce": "^9.0.4", "use-font-face-observer": "^1.2.2", "uuid": "^9.0.0", "zxcvbn": "^4.4.2"