diff --git a/web/components/core/views/index.ts b/web/components/core/views/index.ts index 5a3edcd20..84e9b6e16 100644 --- a/web/components/core/views/index.ts +++ b/web/components/core/views/index.ts @@ -1,3 +1,2 @@ -export * from "./spreadsheet-view"; export * from "./issues-view"; export * from "./inline-issue-create-wrapper"; diff --git a/web/components/core/views/spreadsheet-view/assignee-column/assignee-column.tsx b/web/components/core/views/spreadsheet-view/assignee-column/assignee-column.tsx deleted file mode 100644 index 1685657cf..000000000 --- a/web/components/core/views/spreadsheet-view/assignee-column/assignee-column.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from "react"; -import { useRouter } from "next/router"; -// components -import { MembersSelect } from "components/project"; -// services -import { TrackEventService } from "services/track_event.service"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - onChange: (formData: Partial) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -const trackEventService = new TrackEventService(); - -export const AssigneeColumn: React.FC = ({ issue, projectId, onChange, properties, user, isNotAllowed }) => { - const router = useRouter(); - - const { workspaceSlug } = router.query; - - const handleAssigneeChange = (data: any) => { - const newData = issue.assignees ?? []; - - if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); - else newData.push(data); - - onChange({ assignees_list: data }); - - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_ASSIGNEE", - user as IUser - ); - }; - - return ( -
- - {properties.assignee && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/assignee-column/spreadsheet-assignee-column.tsx b/web/components/core/views/spreadsheet-view/assignee-column/spreadsheet-assignee-column.tsx deleted file mode 100644 index 8d34a8a42..000000000 --- a/web/components/core/views/spreadsheet-view/assignee-column/spreadsheet-assignee-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { AssigneeColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - handleUpdateIssue: (issueId: string, data: Partial) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetAssigneeColumn: React.FC = ({ - issue, - projectId, - handleUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- handleUpdateIssue(issue.id, data)} - user={user} - isNotAllowed={isNotAllowed} - /> - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/created-on-column/created-on-column.tsx b/web/components/core/views/spreadsheet-view/created-on-column/created-on-column.tsx deleted file mode 100644 index 12c33abfc..000000000 --- a/web/components/core/views/spreadsheet-view/created-on-column/created-on-column.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; - -// types -import { IIssue, Properties } from "types"; -// helper -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; - -type Props = { - issue: IIssue; - properties: Properties; -}; - -export const CreatedOnColumn: React.FC = ({ issue, properties }) => ( -
- - {properties.created_on && ( -
- {renderLongDetailDateFormat(issue.created_at)} -
- )} -
-
-); diff --git a/web/components/core/views/spreadsheet-view/created-on-column/spreadsheet-created-on-column.tsx b/web/components/core/views/spreadsheet-view/created-on-column/spreadsheet-created-on-column.tsx deleted file mode 100644 index cf52091e6..000000000 --- a/web/components/core/views/spreadsheet-view/created-on-column/spreadsheet-created-on-column.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from "react"; - -// components -import { CreatedOnColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - handleUpdateIssue: (formData: Partial) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetCreatedOnColumn: React.FC = ({ - issue, - handleUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/due-date-column/due-date-column.tsx b/web/components/core/views/spreadsheet-view/due-date-column/due-date-column.tsx deleted file mode 100644 index 3c07b0632..000000000 --- a/web/components/core/views/spreadsheet-view/due-date-column/due-date-column.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { FC } from "react"; -// components -import { ViewDueDateSelect } from "components/issues"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const DueDateColumn: FC = (props) => { - const { issue, partialUpdateIssue, properties, user, isNotAllowed } = props; - return ( -
- - {properties.due_date && user && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/due-date-column/spreadsheet-due-date-column.tsx b/web/components/core/views/spreadsheet-view/due-date-column/spreadsheet-due-date-column.tsx deleted file mode 100644 index 2187cf02d..000000000 --- a/web/components/core/views/spreadsheet-view/due-date-column/spreadsheet-due-date-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { DueDateColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetDueDateColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/estimate-column/estimate-column.tsx b/web/components/core/views/spreadsheet-view/estimate-column/estimate-column.tsx deleted file mode 100644 index 4f43d572f..000000000 --- a/web/components/core/views/spreadsheet-view/estimate-column/estimate-column.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { FC } from "react"; -// components -import { ViewEstimateSelect } from "components/issues"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const EstimateColumn: FC = (props) => { - const { issue, partialUpdateIssue, properties, user, isNotAllowed } = props; - - return ( -
- - {properties.estimate && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/estimate-column/spreadsheet-estimate-column.tsx b/web/components/core/views/spreadsheet-view/estimate-column/spreadsheet-estimate-column.tsx deleted file mode 100644 index 7ff76f67d..000000000 --- a/web/components/core/views/spreadsheet-view/estimate-column/spreadsheet-estimate-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { EstimateColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetEstimateColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/index.ts b/web/components/core/views/spreadsheet-view/index.ts deleted file mode 100644 index 9bf8ed1b0..000000000 --- a/web/components/core/views/spreadsheet-view/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from "./assignee-column"; -export * from "./created-on-column"; -export * from "./due-date-column"; -export * from "./estimate-column"; -export * from "./issue-column"; -export * from "./label-column"; -export * from "./priority-column"; -export * from "./start-date-column"; -export * from "./state-column"; -export * from "./updated-on-column"; -export * from "./spreadsheet-view"; -export * from "./issue-column/issue-column"; -export * from "./issue-column/spreadsheet-issue-column"; diff --git a/web/components/core/views/spreadsheet-view/label-column/label-column.tsx b/web/components/core/views/spreadsheet-view/label-column/label-column.tsx deleted file mode 100644 index e626cc42a..000000000 --- a/web/components/core/views/spreadsheet-view/label-column/label-column.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; - -// components -import { LabelSelect } from "components/project"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const LabelColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - properties, - user, - isNotAllowed, -}) => { - const handleLabelChange = (data: any) => { - partialUpdateIssue({ labels_list: data }, issue); - }; - - return ( -
- - {properties.labels && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/label-column/spreadsheet-label-column.tsx b/web/components/core/views/spreadsheet-view/label-column/spreadsheet-label-column.tsx deleted file mode 100644 index fbf4e9458..000000000 --- a/web/components/core/views/spreadsheet-view/label-column/spreadsheet-label-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { LabelColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetLabelColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/priority-column/priority-column.tsx b/web/components/core/views/spreadsheet-view/priority-column/priority-column.tsx deleted file mode 100644 index 75e7ee0ac..000000000 --- a/web/components/core/views/spreadsheet-view/priority-column/priority-column.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { FC } from "react"; -import { useRouter } from "next/router"; -// components -import { PrioritySelect } from "components/project"; -// services -import { TrackEventService } from "services/track_event.service"; -// types -import { IUser, IIssue, Properties, TIssuePriorities } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -const trackEventService = new TrackEventService(); - -export const PriorityColumn: FC = (props) => { - const { issue, partialUpdateIssue, properties, user, isNotAllowed } = props; - // router - const router = useRouter(); - const { workspaceSlug } = router.query; - - const handlePriorityChange = (data: TIssuePriorities) => { - partialUpdateIssue({ priority: data }, issue); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_PRIORITY", - user as IUser - ); - }; - - return ( -
- - {properties.priority && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/priority-column/spreadsheet-priority-column.tsx b/web/components/core/views/spreadsheet-view/priority-column/spreadsheet-priority-column.tsx deleted file mode 100644 index 39f88fd26..000000000 --- a/web/components/core/views/spreadsheet-view/priority-column/spreadsheet-priority-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { PriorityColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetPriorityColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/single-issue.tsx b/web/components/core/views/spreadsheet-view/single-issue.tsx deleted file mode 100644 index 0c1ac8744..000000000 --- a/web/components/core/views/spreadsheet-view/single-issue.tsx +++ /dev/null @@ -1,448 +0,0 @@ -import { FC, useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { mutate } from "swr"; -import { Popover2 } from "@blueprintjs/popover2"; -// components -import { ViewDueDateSelect, ViewEstimateSelect, ViewStartDateSelect } from "components/issues"; -import { LabelSelect, MembersSelect, PrioritySelect } from "components/project"; -import { StateSelect } from "components/states"; -// icons -import { MoreHorizontal, LinkIcon, Pencil, Trash2, ChevronRight } from "lucide-react"; -// services -import { IssueService } from "services/issue"; -import { TrackEventService } from "services/track_event.service"; -// constant -import { - CYCLE_DETAILS, - CYCLE_ISSUES_WITH_PARAMS, - MODULE_DETAILS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - SUB_ISSUES, - VIEW_ISSUES, -} from "constants/fetch-keys"; -// types -import { IUser, IIssue, IState, ISubIssueResponse, Properties, TIssuePriorities, UserAuth } from "types"; -// helper -import { copyTextToClipboard } from "helpers/string.helper"; -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; -// hooks -import useToast from "hooks/use-toast"; - -type Props = { - issue: IIssue; - projectId: string; - index: number; - expanded: boolean; - handleToggleExpand: (issueId: string) => void; - properties: Properties; - handleEditIssue: (issue: IIssue) => void; - handleDeleteIssue: (issue: IIssue) => void; - gridTemplateColumns: string; - disableUserActions: boolean; - user: IUser | undefined; - userAuth: UserAuth; - nestingLevel: number; -}; - -const issueService = new IssueService(); -const trackEventService = new TrackEventService(); - -export const SingleSpreadsheetIssue: FC = (props) => { - const { - issue, - projectId, - index, - expanded, - handleToggleExpand, - properties, - handleEditIssue, - handleDeleteIssue, - gridTemplateColumns, - disableUserActions, - user, - userAuth, - nestingLevel, - } = props; - // states - const [isOpen, setIsOpen] = useState(false); - // router - const router = useRouter(); - const { workspaceSlug, cycleId, moduleId, viewId } = router.query; - - const params = {}; - - const { setToastAlert } = useToast(); - - const partialUpdateIssue = useCallback( - (formData: Partial, issue: IIssue) => { - if (!workspaceSlug || !projectId) return; - - const fetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) - : viewId - ? VIEW_ISSUES(viewId.toString(), params) - : PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params); - - if (issue.parent) - mutate( - SUB_ISSUES(issue.parent.toString()), - (prevData) => { - if (!prevData) return prevData; - - return { - ...prevData, - sub_issues: (prevData.sub_issues ?? []).map((i) => { - if (i.id === issue.id) { - return { - ...i, - ...formData, - }; - } - return i; - }), - }; - }, - false - ); - else - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((p) => { - if (p.id === issue.id) { - return { - ...p, - ...formData, - }; - } - return p; - }), - false - ); - - issueService - .patchIssue(workspaceSlug as string, projectId, issue.id as string, formData, user) - .then(() => { - if (issue.parent) { - mutate(SUB_ISSUES(issue.parent as string)); - } else { - if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); - if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); - } - }) - .catch((error) => { - console.log(error); - }); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [workspaceSlug, projectId, cycleId, moduleId, user] - ); - - const openPeekOverview = () => { - const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssue: issue.id }, - }); - }; - - const handleCopyText = () => { - const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; - copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`).then(() => { - setToastAlert({ - type: "success", - title: "Link Copied!", - message: "Issue link copied to clipboard.", - }); - }); - }; - - const handleStateChange = (data: string, states: IState[] | undefined) => { - const oldState = states?.find((s) => s.id === issue.state); - const newState = states?.find((s) => s.id === data); - - partialUpdateIssue( - { - state: data, - state_detail: newState, - }, - issue - ); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_STATE", - user as IUser - ); - if (oldState?.group !== "completed" && newState?.group !== "completed") { - trackEventService.trackIssueMarkedAsDoneEvent( - { - workspaceSlug: issue.workspace_detail.slug, - workspaceId: issue.workspace_detail.id, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - user as IUser - ); - } - }; - - const handlePriorityChange = (data: TIssuePriorities) => { - partialUpdateIssue({ priority: data }, issue); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_PRIORITY", - user as IUser - ); - }; - - const handleAssigneeChange = (data: any) => { - const newData = issue.assignees ?? []; - - if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); - else newData.push(data); - - partialUpdateIssue({ assignees_list: data }, issue); - - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_ASSIGNEE", - user as IUser - ); - }; - - const handleLabelChange = (data: any) => { - partialUpdateIssue({ labels_list: data }, issue); - }; - - const paddingLeft = `${nestingLevel * 68}px`; - - const tooltipPosition = index === 0 ? "bottom" : "top"; - - const isNotAllowed = userAuth.isGuest || userAuth.isViewer; - - return ( - <> -
-
-
-
- {properties.key && ( - - {issue.project_detail?.identifier}-{issue.sequence_id} - - )} - {!isNotAllowed && !disableUserActions && ( -
- setIsOpen(nextOpenState)} - content={ -
- - - - - -
- } - placement="bottom-start" - > - -
-
- )} -
- - {issue.sub_issues_count > 0 && ( -
- -
- )} -
- - -
- {properties.state && ( -
- -
- )} - {properties.priority && ( -
- -
- )} - {properties.assignee && ( -
- -
- )} - {properties.labels && ( -
- -
- )} - - {properties.start_date && ( -
- -
- )} - - {properties.due_date && ( -
- {user && ( - - )} -
- )} - {properties.estimate && ( -
- -
- )} - {properties.created_on && ( -
- {renderLongDetailDateFormat(issue.created_at)} -
- )} - {properties.updated_on && ( -
- {renderLongDetailDateFormat(issue.updated_at)} -
- )} -
- - ); -}; diff --git a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx b/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx deleted file mode 100644 index 707340c64..000000000 --- a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx +++ /dev/null @@ -1,402 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// hooks -import useLocalStorage from "hooks/use-local-storage"; -// components -import { - // ListInlineCreateIssueForm, - SpreadsheetAssigneeColumn, - SpreadsheetCreatedOnColumn, - SpreadsheetDueDateColumn, - SpreadsheetEstimateColumn, - SpreadsheetIssuesColumn, - SpreadsheetLabelColumn, - SpreadsheetPriorityColumn, - SpreadsheetStartDateColumn, - SpreadsheetStateColumn, - SpreadsheetUpdatedOnColumn, -} from "components/core"; -import { CustomMenu } from "components/ui"; -import { IssuePeekOverview } from "components/issues"; -import { Spinner } from "@plane/ui"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssueOrderByOptions } from "types"; -// icon -import { - ArrowDownWideNarrow, - ArrowUpNarrowWide, - CheckIcon, - ChevronDownIcon, - Eraser, - ListFilter, - MoveRight, - PlusIcon, -} from "lucide-react"; - -type Props = { - displayProperties: IIssueDisplayProperties; - displayFilters: IIssueDisplayFilterOptions; - handleDisplayFilterUpdate: (data: Partial) => void; - issues: IIssue[] | undefined; - handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; - handleUpdateIssue: (issueId: string, data: Partial) => void; - openIssuesListModal?: (() => void) | null; - disableUserActions: boolean; -}; - -export const SpreadsheetView: React.FC = observer((props) => { - const { - displayProperties, - displayFilters, - handleDisplayFilterUpdate, - issues, - handleIssueAction, - handleUpdateIssue, - openIssuesListModal, - disableUserActions, - } = props; - - const [expandedIssues, setExpandedIssues] = useState([]); - const [currentProjectId, setCurrentProjectId] = useState(null); - - const [isInlineCreateIssueFormOpen, setIsInlineCreateIssueFormOpen] = useState(false); - - const [isScrolled, setIsScrolled] = useState(false); - - const containerRef = useRef(null); - - const router = useRouter(); - const { workspaceSlug, cycleId, moduleId } = router.query; - - const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; - - const { storedValue: selectedMenuItem, setValue: setSelectedMenuItem } = useLocalStorage( - "spreadsheetViewSorting", - "" - ); - const { storedValue: activeSortingProperty, setValue: setActiveSortingProperty } = useLocalStorage( - "spreadsheetViewActiveSortingProperty", - "" - ); - - const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => { - handleDisplayFilterUpdate({ order_by: order }); - - setSelectedMenuItem(`${order}_${itemKey}`); - setActiveSortingProperty(order === "-created_at" ? "" : itemKey); - }; - - const renderColumn = ( - header: string, - propertyName: string, - Component: React.ComponentType, - ascendingOrder: TIssueOrderByOptions, - descendingOrder: TIssueOrderByOptions - ) => ( -
-
- - {activeSortingProperty === propertyName && ( -
- -
- )} - - {header} -
- } - width="xl" - > - { - handleOrderBy(ascendingOrder, propertyName); - }} - > -
-
- {propertyName === "assignee" || propertyName === "labels" ? ( - <> - - A - - Z - - ) : propertyName === "due_date" || propertyName === "created_on" || propertyName === "updated_on" ? ( - <> - - New - - Old - - ) : ( - <> - - First - - Last - - )} -
- - -
-
- { - handleOrderBy(descendingOrder, propertyName); - }} - > -
-
- {propertyName === "assignee" || propertyName === "labels" ? ( - <> - - Z - - A - - ) : propertyName === "due_date" ? ( - <> - - Old - - New - - ) : ( - <> - - Last - - First - - )} -
- - -
-
- {selectedMenuItem && - selectedMenuItem !== "" && - displayFilters?.order_by !== "-created_at" && - selectedMenuItem.includes(propertyName) && ( - { - handleOrderBy("-created_at", propertyName); - }} - > -
-
- - - - - Clear sorting -
-
-
- )} - -
-
- {issues?.map((issue) => ( - - ))} -
- - ); - - const handleScroll = () => { - if (containerRef.current) { - const scrollLeft = containerRef.current.scrollLeft; - setIsScrolled(scrollLeft > 0); - } - }; - - useEffect(() => { - const currentContainerRef = containerRef.current; - - if (currentContainerRef) { - currentContainerRef.addEventListener("scroll", handleScroll); - } - - return () => { - if (currentContainerRef) { - currentContainerRef.removeEventListener("scroll", handleScroll); - } - }; - }, []); - - return ( - <> - -
-
-
- {issues ? ( - <> -
-
-
- {displayProperties.key && ( - ID - )} - Issue -
- - {issues.map((issue: IIssue, index) => ( - - ))} -
-
- {displayProperties.state && - renderColumn("State", "state", SpreadsheetStateColumn, "state__name", "-state__name")} - - {displayProperties.priority && - renderColumn("Priority", "priority", SpreadsheetPriorityColumn, "priority", "-priority")} - {displayProperties.assignee && - renderColumn( - "Assignees", - "assignee", - SpreadsheetAssigneeColumn, - "assignees__first_name", - "-assignees__first_name" - )} - {displayProperties.labels && - renderColumn("Label", "labels", SpreadsheetLabelColumn, "labels__name", "-labels__name")} - {displayProperties.start_date && - renderColumn("Start Date", "start_date", SpreadsheetStartDateColumn, "-start_date", "start_date")} - {displayProperties.due_date && - renderColumn("Due Date", "due_date", SpreadsheetDueDateColumn, "-target_date", "target_date")} - {displayProperties.estimate && - renderColumn("Estimate", "estimate", SpreadsheetEstimateColumn, "estimate_point", "-estimate_point")} - {displayProperties.created_on && - renderColumn("Created On", "created_on", SpreadsheetCreatedOnColumn, "-created_at", "created_at")} - {displayProperties.updated_on && - renderColumn("Updated On", "updated_on", SpreadsheetUpdatedOnColumn, "-updated_at", "updated_at")} - - ) : ( -
- -
- )} -
- -
-
- {/* setIsInlineCreateIssueFormOpen(false)} - prePopulatedData={{ - ...(cycleId && { cycle: cycleId.toString() }), - ...(moduleId && { module: moduleId.toString() }), - }} - /> */} -
- - {type === "issue" - ? !disableUserActions && - !isInlineCreateIssueFormOpen && ( - - ) - : !disableUserActions && - !isInlineCreateIssueFormOpen && ( - - - New Issue - - } - optionsClassName="left-5 !w-36" - noBorder - > - setIsInlineCreateIssueFormOpen(true)}> - Create new - - {openIssuesListModal && ( - Add an existing issue - )} - - )} -
-
-
- - ); -}); diff --git a/web/components/core/views/spreadsheet-view/start-date-column/spreadsheet-start-date-column.tsx b/web/components/core/views/spreadsheet-view/start-date-column/spreadsheet-start-date-column.tsx deleted file mode 100644 index b239fd880..000000000 --- a/web/components/core/views/spreadsheet-view/start-date-column/spreadsheet-start-date-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { StartDateColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetStartDateColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/start-date-column/start-date-column.tsx b/web/components/core/views/spreadsheet-view/start-date-column/start-date-column.tsx deleted file mode 100644 index 87b3dc838..000000000 --- a/web/components/core/views/spreadsheet-view/start-date-column/start-date-column.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from "react"; -// components -import { ViewStartDateSelect } from "components/issues"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const StartDateColumn: React.FC = (props) => { - const { issue, partialUpdateIssue, properties, user, isNotAllowed } = props; - return ( -
- - {properties.due_date && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/state-column/spreadsheet-state-column.tsx b/web/components/core/views/spreadsheet-view/state-column/spreadsheet-state-column.tsx deleted file mode 100644 index d0e3792ee..000000000 --- a/web/components/core/views/spreadsheet-view/state-column/spreadsheet-state-column.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from "react"; - -// components -import { StateColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - handleUpdateIssue: (issueId: string, data: Partial) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetStateColumn: React.FC = ({ - issue, - projectId, - handleUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- {user && ( - handleUpdateIssue(issue.id, data)} - user={user} - isNotAllowed={isNotAllowed} - /> - )} - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/state-column/state-column.tsx b/web/components/core/views/spreadsheet-view/state-column/state-column.tsx deleted file mode 100644 index 68f381edf..000000000 --- a/web/components/core/views/spreadsheet-view/state-column/state-column.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from "react"; -import { useRouter } from "next/router"; -// components -import { StateSelect } from "components/states"; -// services -import { TrackEventService } from "services/track_event.service"; -// types -import { IUser, IIssue, IState, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - onChange: (formData: Partial) => void; - properties: Properties; - user: IUser; - isNotAllowed: boolean; -}; - -const trackEventService = new TrackEventService(); - -export const StateColumn: React.FC = ({ issue, projectId, onChange, properties, user, isNotAllowed }) => { - const router = useRouter(); - - const { workspaceSlug } = router.query; - - const handleStateChange = (data: string, states: IState[] | undefined) => { - const oldState = states?.find((s) => s.id === issue.state); - const newState = states?.find((s) => s.id === data); - - onChange({ - state: data, - state_detail: newState, - }); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_STATE", - user - ); - if (oldState?.group !== "completed" && newState?.group !== "completed") { - trackEventService.trackIssueMarkedAsDoneEvent( - { - workspaceSlug: issue.workspace_detail.slug, - workspaceId: issue.workspace_detail.id, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - user - ); - } - }; - - return ( -
- - {properties.state && ( - - )} - -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/updated-on-column/spreadsheet-updated-on-column.tsx b/web/components/core/views/spreadsheet-view/updated-on-column/spreadsheet-updated-on-column.tsx deleted file mode 100644 index 1df6d7bbe..000000000 --- a/web/components/core/views/spreadsheet-view/updated-on-column/spreadsheet-updated-on-column.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from "react"; - -// components -import { UpdatedOnColumn } from "components/core"; -// hooks -import useSubIssue from "hooks/use-sub-issue"; -// types -import { IUser, IIssue, Properties } from "types"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - expandedIssues: string[]; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const SpreadsheetUpdatedOnColumn: React.FC = ({ - issue, - projectId, - partialUpdateIssue, - expandedIssues, - properties, - user, - isNotAllowed, -}) => { - const isExpanded = expandedIssues.indexOf(issue.id) > -1; - - const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); - - return ( -
- - - {isExpanded && - !isLoading && - subIssues && - subIssues.length > 0 && - subIssues.map((subIssue: IIssue) => ( - - ))} -
- ); -}; diff --git a/web/components/core/views/spreadsheet-view/updated-on-column/updated-on-column.tsx b/web/components/core/views/spreadsheet-view/updated-on-column/updated-on-column.tsx deleted file mode 100644 index a108d17c3..000000000 --- a/web/components/core/views/spreadsheet-view/updated-on-column/updated-on-column.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { FC } from "react"; -// types -import { IUser, IIssue, Properties } from "types"; -// helper -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; - -type Props = { - issue: IIssue; - projectId: string; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - properties: Properties; - user: IUser | undefined; - isNotAllowed: boolean; -}; - -export const UpdatedOnColumn: FC = (props) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { issue, projectId, partialUpdateIssue, properties, user, isNotAllowed } = props; - return ( -
- - {properties.updated_on && ( -
- {renderLongDetailDateFormat(issue.updated_at)} -
- )} -
-
- ); -}; diff --git a/web/components/issues/issue-layouts/roots/global-view-layout-root.tsx b/web/components/issues/issue-layouts/roots/global-view-layout-root.tsx index ff72eae3c..4886339aa 100644 --- a/web/components/issues/issue-layouts/roots/global-view-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/global-view-layout-root.tsx @@ -6,10 +6,9 @@ import useSWR from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { SpreadsheetView } from "components/core"; -import { GlobalViewsAppliedFiltersRoot } from "components/issues"; +import { GlobalViewsAppliedFiltersRoot, SpreadsheetView } from "components/issues"; // types -import { IIssueDisplayFilterOptions, TStaticViewTypes } from "types"; +import { IIssue, IIssueDisplayFilterOptions, TStaticViewTypes } from "types"; // fetch-keys import { GLOBAL_VIEW_ISSUES } from "constants/fetch-keys"; @@ -28,6 +27,7 @@ export const GlobalViewLayoutRoot: React.FC = observer((props) => { globalViewIssues: globalViewIssuesStore, globalViewFilters: globalViewFiltersStore, workspaceFilter: workspaceFilterStore, + workspace: workspaceStore, } = useMobxStore(); const viewDetails = globalViewId ? globalViewsStore.globalViewDetails[globalViewId.toString()] : undefined; @@ -63,6 +63,18 @@ export const GlobalViewLayoutRoot: React.FC = observer((props) => { [workspaceFilterStore, workspaceSlug] ); + const handleUpdateIssue = useCallback( + (issue: IIssue, data: Partial) => { + if (!workspaceSlug) return; + + console.log("issue", issue); + console.log("data", data); + + // TODO: add update issue logic here + }, + [workspaceSlug] + ); + const issues = type ? globalViewIssuesStore.viewIssues?.[type] : globalViewId @@ -78,8 +90,10 @@ export const GlobalViewLayoutRoot: React.FC = observer((props) => { displayFilters={workspaceFilterStore.workspaceDisplayFilters} handleDisplayFilterUpdate={handleDisplayFiltersUpdate} issues={issues} + members={workspaceStore.workspaceMembers ? workspaceStore.workspaceMembers.map((m) => m.member) : undefined} + labels={workspaceStore.workspaceLabels ? workspaceStore.workspaceLabels : undefined} handleIssueAction={() => {}} - handleUpdateIssue={() => {}} + handleUpdateIssue={handleUpdateIssue} disableUserActions={false} /> diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index 576c6f9f2..25a808450 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -12,32 +12,27 @@ import { GanttLayout, KanBanLayout, ProjectAppliedFiltersRoot, - SpreadsheetLayout, + ProjectSpreadsheetLayout, } from "components/issues"; export const ProjectLayoutRoot: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { - workspaceSlug: string; - projectId: string; - cycleId: string; - moduleId: string; - }; + const { workspaceSlug, projectId } = router.query; const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore } = useMobxStore(); useSWR( - workspaceSlug && projectId ? `PROJECT_ISSUES` : null, + workspaceSlug && projectId ? `REVALIDATE_PROJECT_ISSUES_${projectId.toString()}` : null, async () => { if (workspaceSlug && projectId) { - await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId); + await issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()); - await projectStore.fetchProjectStates(workspaceSlug, projectId); - await projectStore.fetchProjectLabels(workspaceSlug, projectId); - await projectStore.fetchProjectMembers(workspaceSlug, projectId); - await projectStore.fetchProjectEstimates(workspaceSlug, projectId); + await projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString()); + await projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString()); + await projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString()); + await projectStore.fetchProjectEstimates(workspaceSlug.toString(), projectId.toString()); - await issueStore.fetchIssues(workspaceSlug, projectId); + await issueStore.fetchIssues(workspaceSlug.toString(), projectId.toString()); } }, { revalidateOnFocus: false } @@ -58,7 +53,7 @@ export const ProjectLayoutRoot: React.FC = observer(() => { ) : activeLayout === "gantt_chart" ? ( ) : activeLayout === "spreadsheet" ? ( - + ) : null} diff --git a/web/components/issues/issue-layouts/spreadsheet/assignee-column/assignee-column.tsx b/web/components/issues/issue-layouts/spreadsheet/assignee-column/assignee-column.tsx new file mode 100644 index 000000000..53484b3f6 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/assignee-column/assignee-column.tsx @@ -0,0 +1,32 @@ +import React from "react"; +// components +import { MembersSelect } from "components/project"; +// types +import { IIssue, IUserLite } from "types"; + +type Props = { + issue: IIssue; + onChange: (members: string[]) => void; + members: IUserLite[] | undefined; + disabled: boolean; +}; + +export const AssigneeColumn: React.FC = (props) => { + const { issue, onChange, members, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/core/views/spreadsheet-view/assignee-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/assignee-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/assignee-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/assignee-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/assignee-column/spreadsheet-assignee-column.tsx b/web/components/issues/issue-layouts/spreadsheet/assignee-column/spreadsheet-assignee-column.tsx new file mode 100644 index 000000000..f0f10880b --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/assignee-column/spreadsheet-assignee-column.tsx @@ -0,0 +1,48 @@ +import React from "react"; + +// components +import { AssigneeColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue, IUserLite } from "types"; + +type Props = { + issue: IIssue; + members: IUserLite[] | undefined; + onChange: (data: Partial) => void; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetAssigneeColumn: React.FC = ({ issue, members, onChange, expandedIssues, disabled }) => { + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ assignees_list: data })} + disabled={disabled} + /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/created-on-column/created-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/created-on-column/created-on-column.tsx new file mode 100644 index 000000000..fe648ac86 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/created-on-column/created-on-column.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +// types +import { IIssue } from "types"; +// helper +import { renderLongDetailDateFormat } from "helpers/date-time.helper"; + +type Props = { + issue: IIssue; +}; + +export const CreatedOnColumn: React.FC = ({ issue }) => ( +
+ +
+ {renderLongDetailDateFormat(issue.created_at)} +
+
+
+); diff --git a/web/components/core/views/spreadsheet-view/created-on-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/created-on-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/created-on-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/created-on-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/created-on-column/spreadsheet-created-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/created-on-column/spreadsheet-created-on-column.tsx new file mode 100644 index 000000000..5295a8648 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/created-on-column/spreadsheet-created-on-column.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +// components +import { CreatedOnColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + expandedIssues: string[]; +}; + +export const SpreadsheetCreatedOnColumn: React.FC = ({ issue, expandedIssues }) => { + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/cycle-root.tsx b/web/components/issues/issue-layouts/spreadsheet/cycle-root.tsx deleted file mode 100644 index 0b74ecf0d..000000000 --- a/web/components/issues/issue-layouts/spreadsheet/cycle-root.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; -// components -// import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { IssuePeekOverview } from "components/issues"; -// ui -import { CustomMenu } from "components/ui"; -import { Spinner } from "@plane/ui"; -// icon -import { Plus } from "lucide-react"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; -import { IIssueUnGroupedStructure } from "store/issue"; -// constants -import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; - -export const CycleSpreadsheetLayout: React.FC = observer(() => { - const [expandedIssues, setExpandedIssues] = useState([]); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); - - const { cycleIssue: cycleIssueStore, issueFilter: issueFilterStore } = useMobxStore(); - - const issues = cycleIssueStore.getIssues; - const issueDisplayProperties = issueFilterStore.userDisplayProperties; - - const handleDisplayFiltersUpdate = useCallback( - (updatedDisplayFilter: Partial) => { - if (!workspaceSlug || !projectId) return; - - issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { - display_filters: { - ...updatedDisplayFilter, - }, - }); - }, - [issueFilterStore, projectId, workspaceSlug] - ); - - const columnData = SPREADSHEET_COLUMN.map((column) => ({ - ...column, - isActive: issueDisplayProperties - ? column.propertyName === "labels" - ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : column.propertyName === "title" - ? true - : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : false, - })); - - const gridTemplateColumns = columnData - .filter((column) => column.isActive) - .map((column) => column.colSize) - .join(" "); - - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - - return ( - <> - -
-
- {/* */} -
- {issues ? ( -
- {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( - <> - // {}} - // disableUserActions={!isAllowed} - // user={user} - // userAuth={{ - // isViewer: projectDetails?.member_role === 5, - // isGuest: projectDetails?.member_role === 10, - // isMember: projectDetails?.member_role === 15, - // isOwner: projectDetails?.member_role === 20, - // }} - // /> - ))} -
- {isAllowed && ( - - - Add Issue - - } - optionsClassName="left-5 !w-36" - noBorder - > - { - const e = new KeyboardEvent("keydown", { key: "c" }); - document.dispatchEvent(e); - }} - > - Create new - - {true && {}}>Add an existing issue} - - )} -
-
- ) : ( - - )} -
- - ); -}); diff --git a/web/components/issues/issue-layouts/spreadsheet/due-date-column/due-date-column.tsx b/web/components/issues/issue-layouts/spreadsheet/due-date-column/due-date-column.tsx new file mode 100644 index 000000000..0caca7cd7 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/due-date-column/due-date-column.tsx @@ -0,0 +1,23 @@ +import { FC } from "react"; +// components +import { ViewDueDateSelect } from "components/issues"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (date: string | null) => void; + disabled: boolean; +}; + +export const DueDateColumn: FC = (props) => { + const { issue, onChange, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/core/views/spreadsheet-view/due-date-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/due-date-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/due-date-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/due-date-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/due-date-column/spreadsheet-due-date-column.tsx b/web/components/issues/issue-layouts/spreadsheet/due-date-column/spreadsheet-due-date-column.tsx new file mode 100644 index 000000000..0693ac455 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/due-date-column/spreadsheet-due-date-column.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +// components +import { DueDateColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: Partial) => void; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetDueDateColumn: React.FC = ({ issue, onChange, expandedIssues, disabled }) => { + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ target_date: val })} disabled={disabled} /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/estimate-column/estimate-column.tsx b/web/components/issues/issue-layouts/spreadsheet/estimate-column/estimate-column.tsx new file mode 100644 index 000000000..f03011135 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/estimate-column/estimate-column.tsx @@ -0,0 +1,23 @@ +import { FC } from "react"; +// components +import { ViewEstimateSelect } from "components/issues"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: number) => void; + disabled: boolean; +}; + +export const EstimateColumn: FC = (props) => { + const { issue, onChange, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/core/views/spreadsheet-view/estimate-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/estimate-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/estimate-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/estimate-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/estimate-column/spreadsheet-estimate-column.tsx b/web/components/issues/issue-layouts/spreadsheet/estimate-column/spreadsheet-estimate-column.tsx new file mode 100644 index 000000000..a8ae5beb4 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/estimate-column/spreadsheet-estimate-column.tsx @@ -0,0 +1,41 @@ +// components +import { EstimateColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (formData: Partial) => void; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetEstimateColumn: React.FC = (props) => { + const { issue, onChange, expandedIssues, disabled } = props; + + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ estimate_point: data })} disabled={disabled} /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/index.ts b/web/components/issues/issue-layouts/spreadsheet/index.ts index 89cf064a4..7a804c6de 100644 --- a/web/components/issues/issue-layouts/spreadsheet/index.ts +++ b/web/components/issues/issue-layouts/spreadsheet/index.ts @@ -1,4 +1,15 @@ -export * from "./cycle-root"; -export * from "./module-root"; -export * from "./project-view-root"; -export * from "./root"; +export * from "./assignee-column"; +export * from "./created-on-column"; +export * from "./due-date-column"; +export * from "./estimate-column"; +export * from "./issue-column"; +export * from "./label-column"; +export * from "./priority-column"; +export * from "./roots"; +export * from "./start-date-column"; +export * from "./state-column"; +export * from "./updated-on-column"; +export * from "./issue-column"; +export * from "./spreadsheet-column"; +export * from "./spreadsheet-columns-list"; +export * from "./spreadsheet-view"; diff --git a/web/components/core/views/spreadsheet-view/issue-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/issue-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/issue-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/issue-column/index.ts diff --git a/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx b/web/components/issues/issue-layouts/spreadsheet/issue-column/issue-column.tsx similarity index 80% rename from web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx rename to web/components/issues/issue-layouts/spreadsheet/issue-column/issue-column.tsx index dca055f61..5b6da6871 100644 --- a/web/components/core/views/spreadsheet-view/issue-column/issue-column.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/issue-column/issue-column.tsx @@ -1,17 +1,13 @@ import React, { useState } from "react"; - import { useRouter } from "next/router"; - -// components import { Popover2 } from "@blueprintjs/popover2"; -// icons import { MoreHorizontal, LinkIcon, Pencil, Trash2, ChevronRight } from "lucide-react"; // hooks import useToast from "hooks/use-toast"; +// helpers +import { copyTextToClipboard } from "helpers/string.helper"; // types import { IIssue, Properties } from "types"; -// helper -import { copyTextToClipboard } from "helpers/string.helper"; type Props = { issue: IIssue; @@ -21,7 +17,6 @@ type Props = { properties: Properties; handleEditIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void; - setCurrentProjectId: React.Dispatch>; disableUserActions: boolean; nestingLevel: number; }; @@ -34,7 +29,6 @@ export const IssueColumn: React.FC = ({ properties, handleEditIssue, handleDeleteIssue, - setCurrentProjectId, disableUserActions, nestingLevel, }) => { @@ -48,7 +42,7 @@ export const IssueColumn: React.FC = ({ const openPeekOverview = () => { const { query } = router; - setCurrentProjectId(issue.project_detail.id); + router.push({ pathname: router.pathname, query: { ...query, peekIssue: issue.id }, @@ -87,47 +81,45 @@ export const IssueColumn: React.FC = ({ canEscapeKeyClose onInteraction={(nextOpenState) => setIsOpen(nextOpenState)} content={ -
+
diff --git a/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx b/web/components/issues/issue-layouts/spreadsheet/issue-column/spreadsheet-issue-column.tsx similarity index 85% rename from web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx rename to web/components/issues/issue-layouts/spreadsheet/issue-column/spreadsheet-issue-column.tsx index 2ba00d903..5500e77e6 100644 --- a/web/components/core/views/spreadsheet-view/issue-column/spreadsheet-issue-column.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/issue-column/spreadsheet-issue-column.tsx @@ -1,7 +1,7 @@ import React from "react"; // components -import { IssueColumn } from "components/core"; +import { IssueColumn } from "components/issues"; // hooks import useSubIssue from "hooks/use-sub-issue"; // types @@ -14,7 +14,6 @@ type Props = { setExpandedIssues: React.Dispatch>; properties: Properties; handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; - setCurrentProjectId: React.Dispatch>; disableUserActions: boolean; nestingLevel?: number; }; @@ -26,7 +25,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ setExpandedIssues, properties, handleIssueAction, - setCurrentProjectId, disableUserActions, nestingLevel = 0, }) => { @@ -34,11 +32,10 @@ export const SpreadsheetIssuesColumn: React.FC = ({ setExpandedIssues((prevState) => { const newArray = [...prevState]; const index = newArray.indexOf(issueId); - if (index > -1) { - newArray.splice(index, 1); - } else { - newArray.push(issueId); - } + + if (index > -1) newArray.splice(index, 1); + else newArray.push(issueId); + return newArray; }); }; @@ -57,7 +54,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ properties={properties} handleEditIssue={() => handleIssueAction(issue, "edit")} handleDeleteIssue={() => handleIssueAction(issue, "delete")} - setCurrentProjectId={setCurrentProjectId} disableUserActions={disableUserActions} nestingLevel={nestingLevel} /> @@ -75,7 +71,6 @@ export const SpreadsheetIssuesColumn: React.FC = ({ setExpandedIssues={setExpandedIssues} properties={properties} handleIssueAction={handleIssueAction} - setCurrentProjectId={setCurrentProjectId} disableUserActions={disableUserActions} nestingLevel={nestingLevel + 1} /> diff --git a/web/components/core/views/spreadsheet-view/label-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/label-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/label-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/label-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/label-column/label-column.tsx b/web/components/issues/issue-layouts/spreadsheet/label-column/label-column.tsx new file mode 100644 index 000000000..3d720da1b --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/label-column/label-column.tsx @@ -0,0 +1,32 @@ +import React from "react"; + +// components +import { LabelSelect } from "components/project"; +// types +import { IIssue, IIssueLabels } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: string[]) => void; + labels: IIssueLabels[] | undefined; + disabled: boolean; +}; + +export const LabelColumn: React.FC = (props) => { + const { issue, onChange, labels, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/label-column/spreadsheet-label-column.tsx b/web/components/issues/issue-layouts/spreadsheet/label-column/spreadsheet-label-column.tsx new file mode 100644 index 000000000..465326fcb --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/label-column/spreadsheet-label-column.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +// components +import { LabelColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue, IIssueLabels } from "types"; + +type Props = { + issue: IIssue; + onChange: (formData: Partial) => void; + labels: IIssueLabels[] | undefined; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetLabelColumn: React.FC = (props) => { + const { issue, onChange, labels, expandedIssues, disabled } = props; + + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ labels_list: data })} + labels={labels} + disabled={disabled} + /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/module-root.tsx b/web/components/issues/issue-layouts/spreadsheet/module-root.tsx deleted file mode 100644 index a72cf40fe..000000000 --- a/web/components/issues/issue-layouts/spreadsheet/module-root.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; -// components -// import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { IssuePeekOverview } from "components/issues"; -// ui -import { CustomMenu } from "components/ui"; -import { Spinner } from "@plane/ui"; -// icon -import { Plus } from "lucide-react"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; -import { IIssueUnGroupedStructure } from "store/issue"; -// constants -import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; - -export const ModuleSpreadsheetLayout: React.FC = observer(() => { - const [expandedIssues, setExpandedIssues] = useState([]); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); - - const { moduleIssue: moduleIssueStore, issueFilter: issueFilterStore } = useMobxStore(); - - const issues = moduleIssueStore.getIssues; - const issueDisplayProperties = issueFilterStore.userDisplayProperties; - - const handleDisplayFiltersUpdate = useCallback( - (updatedDisplayFilter: Partial) => { - if (!workspaceSlug || !projectId) return; - - issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { - display_filters: { - ...updatedDisplayFilter, - }, - }); - }, - [issueFilterStore, projectId, workspaceSlug] - ); - - const columnData = SPREADSHEET_COLUMN.map((column) => ({ - ...column, - isActive: issueDisplayProperties - ? column.propertyName === "labels" - ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : column.propertyName === "title" - ? true - : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : false, - })); - - const gridTemplateColumns = columnData - .filter((column) => column.isActive) - .map((column) => column.colSize) - .join(" "); - - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - - return ( - <> - -
-
- {/* */} -
- {issues ? ( -
- {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( - <> - // {}} - // disableUserActions={!isAllowed} - // user={user} - // userAuth={{ - // isViewer: projectDetails?.member_role === 5, - // isGuest: projectDetails?.member_role === 10, - // isMember: projectDetails?.member_role === 15, - // isOwner: projectDetails?.member_role === 20, - // }} - // /> - ))} -
- {isAllowed && ( - - - Add Issue - - } - optionsClassName="left-5 !w-36" - noBorder - > - { - const e = new KeyboardEvent("keydown", { key: "c" }); - document.dispatchEvent(e); - }} - > - Create new - - {true && {}}>Add an existing issue} - - )} -
-
- ) : ( - - )} -
- - ); -}); diff --git a/web/components/core/views/spreadsheet-view/priority-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/priority-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/priority-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/priority-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/priority-column/priority-column.tsx b/web/components/issues/issue-layouts/spreadsheet/priority-column/priority-column.tsx new file mode 100644 index 000000000..21f2a5927 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/priority-column/priority-column.tsx @@ -0,0 +1,28 @@ +// components +import { PrioritySelect } from "components/project"; +// types +import { IIssue, TIssuePriorities } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: TIssuePriorities) => void; + disabled: boolean; +}; + +export const PriorityColumn: React.FC = (props) => { + const { issue, onChange, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/priority-column/spreadsheet-priority-column.tsx b/web/components/issues/issue-layouts/spreadsheet/priority-column/spreadsheet-priority-column.tsx new file mode 100644 index 000000000..1c12054f5 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/priority-column/spreadsheet-priority-column.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +// components +import { PriorityColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: Partial) => void; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetPriorityColumn: React.FC = ({ issue, onChange, expandedIssues, disabled }) => { + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ priority: data })} disabled={disabled} /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/project-view-root.tsx b/web/components/issues/issue-layouts/spreadsheet/project-view-root.tsx deleted file mode 100644 index ad0a935a6..000000000 --- a/web/components/issues/issue-layouts/spreadsheet/project-view-root.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; -// components -// import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { IssuePeekOverview } from "components/issues"; -// ui -import { Spinner } from "components/ui"; -// icon -import { Plus } from "lucide-react"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; -import { IIssueUnGroupedStructure } from "store/issue"; -// constants -import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; - -export const ProjectViewSpreadsheetLayout: React.FC = observer(() => { - const [expandedIssues, setExpandedIssues] = useState([]); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); - - const { module: moduleStore, issueFilter: issueFilterStore } = useMobxStore(); - - // const issues = moduleStore.issues || []; - const issueDisplayProperties = issueFilterStore.userDisplayProperties; - - const handleDisplayFiltersUpdate = useCallback( - (updatedDisplayFilter: Partial) => { - if (!workspaceSlug || !projectId) return; - - issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { - display_filters: { - ...updatedDisplayFilter, - }, - }); - }, - [issueFilterStore, projectId, workspaceSlug] - ); - - const columnData = SPREADSHEET_COLUMN.map((column) => ({ - ...column, - isActive: issueDisplayProperties - ? column.propertyName === "labels" - ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : column.propertyName === "title" - ? true - : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : false, - })); - - const gridTemplateColumns = columnData - .filter((column) => column.isActive) - .map((column) => column.colSize) - .join(" "); - - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - - return ( - <> - -
-
- {/* */} -
- {/* {issues ? ( -
- {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( - {}} - disableUserActions={!isAllowed} - user={user} - userAuth={{ - isViewer: projectDetails?.member_role === 5, - isGuest: projectDetails?.member_role === 10, - isMember: projectDetails?.member_role === 15, - isOwner: projectDetails?.member_role === 20, - }} - /> - ))} -
- -
-
- ) : ( - - )} */} -
- - ); -}); diff --git a/web/components/issues/issue-layouts/spreadsheet/root.tsx b/web/components/issues/issue-layouts/spreadsheet/root.tsx deleted file mode 100644 index 387419c7b..000000000 --- a/web/components/issues/issue-layouts/spreadsheet/root.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useCallback, useState } from "react"; -import { useRouter } from "next/router"; -import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; -// components -// import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; -import { IssuePeekOverview } from "components/issues"; -// ui -import { CustomMenu } from "components/ui"; -import { Spinner } from "@plane/ui"; -// icon -import { Plus } from "lucide-react"; -// types -import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; -import { IIssueUnGroupedStructure } from "store/issue"; -// constants -import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; - -export const SpreadsheetLayout: React.FC = observer(() => { - const [expandedIssues, setExpandedIssues] = useState([]); - - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); - - const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore(); - - const issues = issueStore.getIssues; - const issueDisplayProperties = issueFilterStore.userDisplayProperties; - - const handleDisplayFiltersUpdate = useCallback( - (updatedDisplayFilter: Partial) => { - if (!workspaceSlug || !projectId) return; - - issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { - display_filters: { - ...updatedDisplayFilter, - }, - }); - }, - [issueFilterStore, projectId, workspaceSlug] - ); - - const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; - - const columnData = SPREADSHEET_COLUMN.map((column) => ({ - ...column, - isActive: issueDisplayProperties - ? column.propertyName === "labels" - ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : column.propertyName === "title" - ? true - : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] - : false, - })); - - const gridTemplateColumns = columnData - .filter((column) => column.isActive) - .map((column) => column.colSize) - .join(" "); - - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - - return ( - <> - -
-
- {/* */} -
- {/* {issues ? ( -
- {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( - {}} - disableUserActions={!isAllowed} - user={user} - userAuth={{ - isViewer: projectDetails?.member_role === 5, - isGuest: projectDetails?.member_role === 10, - isMember: projectDetails?.member_role === 15, - isOwner: projectDetails?.member_role === 20, - }} - /> - ))} -
- {type === "issue" ? ( - - ) : ( - isAllowed && ( - - - Add Issue - - } - position="left" - optionsClassName="left-5 !w-36" - noBorder - > - { - const e = new KeyboardEvent("keydown", { key: "c" }); - document.dispatchEvent(e); - }} - > - Create new - - {true && {}}>Add an existing issue} - - ) - )} -
-
- ) : ( - - )} */} -
- - ); -}); diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx new file mode 100644 index 000000000..c5b75d002 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/roots/cycle-root.tsx @@ -0,0 +1,71 @@ +import React, { useCallback } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { SpreadsheetView } from "components/issues"; +// types +import { IIssue, IIssueDisplayFilterOptions } from "types"; +// constants +import { IIssueUnGroupedStructure } from "store/issue"; + +export const CycleSpreadsheetLayout: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + issueFilter: issueFilterStore, + cycleIssue: cycleIssueStore, + issueDetail: issueDetailStore, + project: projectStore, + user: userStore, + } = useMobxStore(); + + const user = userStore.currentUser; + const issues = cycleIssueStore.getIssues; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const handleUpdateIssue = useCallback( + (issue: IIssue, data: Partial) => { + if (!workspaceSlug || !projectId || !user) return; + + const payload = { + ...issue, + ...data, + }; + + cycleIssueStore.updateIssueStructure(null, null, payload); + issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data, user); + }, + [issueDetailStore, cycleIssueStore, projectId, user, workspaceSlug] + ); + + return ( + m.member) : undefined} + labels={projectId ? projectStore.labels?.[projectId.toString()] ?? undefined : undefined} + states={projectId ? projectStore.states?.[projectId.toString()] : undefined} + handleIssueAction={() => {}} + handleUpdateIssue={handleUpdateIssue} + disableUserActions={false} + /> + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/index.ts b/web/components/issues/issue-layouts/spreadsheet/roots/index.ts new file mode 100644 index 000000000..10037416c --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/roots/index.ts @@ -0,0 +1,4 @@ +export * from "./cycle-root"; +export * from "./module-root"; +export * from "./project-root"; +export * from "./project-view-root"; diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/module-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/module-root.tsx new file mode 100644 index 000000000..f5b222d74 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/roots/module-root.tsx @@ -0,0 +1,71 @@ +import React, { useCallback } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { SpreadsheetView } from "components/issues"; +// types +import { IIssue, IIssueDisplayFilterOptions } from "types"; +// constants +import { IIssueUnGroupedStructure } from "store/issue"; + +export const ModuleSpreadsheetLayout: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + issueFilter: issueFilterStore, + moduleIssue: moduleIssueStore, + issueDetail: issueDetailStore, + project: projectStore, + user: userStore, + } = useMobxStore(); + + const user = userStore.currentUser; + const issues = moduleIssueStore.getIssues; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const handleUpdateIssue = useCallback( + (issue: IIssue, data: Partial) => { + if (!workspaceSlug || !projectId || !user) return; + + const payload = { + ...issue, + ...data, + }; + + moduleIssueStore.updateIssueStructure(null, null, payload); + issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data, user); + }, + [issueDetailStore, moduleIssueStore, projectId, user, workspaceSlug] + ); + + return ( + m.member) : undefined} + labels={projectId ? projectStore.labels?.[projectId.toString()] ?? undefined : undefined} + states={projectId ? projectStore.states?.[projectId.toString()] : undefined} + handleIssueAction={() => {}} + handleUpdateIssue={handleUpdateIssue} + disableUserActions={false} + /> + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/project-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/project-root.tsx new file mode 100644 index 000000000..0341bb241 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/roots/project-root.tsx @@ -0,0 +1,71 @@ +import React, { useCallback } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { SpreadsheetView } from "components/issues"; +// types +import { IIssue, IIssueDisplayFilterOptions } from "types"; +// constants +import { IIssueUnGroupedStructure } from "store/issue"; + +export const ProjectSpreadsheetLayout: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + issue: issueStore, + issueFilter: issueFilterStore, + issueDetail: issueDetailStore, + project: projectStore, + user: userStore, + } = useMobxStore(); + + const user = userStore.currentUser; + const issues = issueStore.getIssues; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const handleUpdateIssue = useCallback( + (issue: IIssue, data: Partial) => { + if (!workspaceSlug || !projectId || !user) return; + + const payload = { + ...issue, + ...data, + }; + + issueStore.updateIssueStructure(null, null, payload); + issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data, user); + }, + [issueStore, issueDetailStore, projectId, user, workspaceSlug] + ); + + return ( + m.member) : undefined} + labels={projectId ? projectStore.labels?.[projectId.toString()] ?? undefined : undefined} + states={projectId ? projectStore.states?.[projectId.toString()] : undefined} + handleIssueAction={() => {}} + handleUpdateIssue={handleUpdateIssue} + disableUserActions={false} + /> + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx b/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx new file mode 100644 index 000000000..509c9de62 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/roots/project-view-root.tsx @@ -0,0 +1,71 @@ +import React, { useCallback } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { SpreadsheetView } from "components/issues"; +// types +import { IIssue, IIssueDisplayFilterOptions } from "types"; +// constants +import { IIssueUnGroupedStructure } from "store/issue"; + +export const ProjectViewSpreadsheetLayout: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + issueFilter: issueFilterStore, + projectViewIssues: projectViewIssueStore, + issueDetail: issueDetailStore, + project: projectStore, + user: userStore, + } = useMobxStore(); + + const user = userStore.currentUser; + const issues = projectViewIssueStore.getIssues; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const handleUpdateIssue = useCallback( + (issue: IIssue, data: Partial) => { + if (!workspaceSlug || !projectId || !user) return; + + const payload = { + ...issue, + ...data, + }; + + projectViewIssueStore.updateIssueStructure(null, null, payload); + issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data, user); + }, + [issueDetailStore, projectViewIssueStore, projectId, user, workspaceSlug] + ); + + return ( + m.member) : undefined} + labels={projectId ? projectStore.labels?.[projectId.toString()] ?? undefined : undefined} + states={projectId ? projectStore.states?.[projectId.toString()] : undefined} + handleIssueAction={() => {}} + handleUpdateIssue={handleUpdateIssue} + disableUserActions={false} + /> + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-column.tsx b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-column.tsx new file mode 100644 index 000000000..d6f3474f3 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-column.tsx @@ -0,0 +1,267 @@ +import { + ArrowDownWideNarrow, + ArrowUpNarrowWide, + CheckIcon, + ChevronDownIcon, + Eraser, + ListFilter, + MoveRight, +} from "lucide-react"; +// hooks +import useLocalStorage from "hooks/use-local-storage"; +// components +import { + SpreadsheetAssigneeColumn, + SpreadsheetCreatedOnColumn, + SpreadsheetDueDateColumn, + SpreadsheetEstimateColumn, + SpreadsheetLabelColumn, + SpreadsheetPriorityColumn, + SpreadsheetStartDateColumn, + SpreadsheetStateColumn, + SpreadsheetUpdatedOnColumn, +} from "components/issues"; +// ui +import { CustomMenu } from "components/ui"; +// types +import { + IIssue, + IIssueDisplayFilterOptions, + IIssueLabels, + IStateResponse, + IUserLite, + TIssueOrderByOptions, +} from "types"; +// constants +import { SPREADSHEET_PROPERTY_DETAILS } from "constants/spreadsheet"; + +type Props = { + disableUserActions: boolean; + displayFilters: IIssueDisplayFilterOptions; + expandedIssues: string[]; + handleDisplayFilterUpdate: (data: Partial) => void; + handleUpdateIssue: (issue: IIssue, data: Partial) => void; + issues: IIssue[] | undefined; + property: string; + members?: IUserLite[] | undefined; + labels?: IIssueLabels[] | undefined; + states?: IStateResponse | undefined; +}; + +export const SpreadsheetColumn: React.FC = (props) => { + const { + disableUserActions, + displayFilters, + expandedIssues, + handleDisplayFilterUpdate, + handleUpdateIssue, + issues, + property, + members, + labels, + states, + } = props; + + const { storedValue: selectedMenuItem, setValue: setSelectedMenuItem } = useLocalStorage( + "spreadsheetViewSorting", + "" + ); + const { storedValue: activeSortingProperty, setValue: setActiveSortingProperty } = useLocalStorage( + "spreadsheetViewActiveSortingProperty", + "" + ); + + const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => { + handleDisplayFilterUpdate({ order_by: order }); + + setSelectedMenuItem(`${order}_${itemKey}`); + setActiveSortingProperty(order === "-created_at" ? "" : itemKey); + }; + + const propertyDetails = SPREADSHEET_PROPERTY_DETAILS[property]; + + return ( +
+
+ +
+ {activeSortingProperty === property && ( +
+ +
+ )} + {propertyDetails.title} +
+
+ } + width="xl" + > + handleOrderBy(propertyDetails.ascendingOrderKey, property)} + > +
+
+ + {propertyDetails.ascendingOrderTitle} + + {propertyDetails.descendingOrderTitle} +
+ + {selectedMenuItem === `${propertyDetails.ascendingOrderKey}_${property}` && ( + + )} +
+
+ handleOrderBy(propertyDetails.descendingOrderKey, property)} + > +
+
+ + {propertyDetails.descendingOrderTitle} + + {propertyDetails.ascendingOrderTitle} +
+ + {selectedMenuItem === `${propertyDetails.descendingOrderKey}_${property}` && ( + + )} +
+
+ {selectedMenuItem && + selectedMenuItem !== "" && + displayFilters?.order_by !== "-created_at" && + selectedMenuItem.includes(property) && ( + handleOrderBy("-created_at", property)} + > +
+ + Clear sorting +
+
+ )} + +
+
+ {issues?.map((issue) => { + if (property === "state") + return ( + ) => handleUpdateIssue(issue, data)} + states={states} + /> + ); + + if (property === "priority") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + + if (property === "estimate") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + if (property === "assignee") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + if (property === "labels") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + if (property === "start_date") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + if (property === "due_date") + return ( + ) => handleUpdateIssue(issue, data)} + /> + ); + if (property === "created_on") + return ( + + ); + if (property === "updated_on") + return ( + + ); + + return null; + })} +
+
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-columns-list.tsx b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-columns-list.tsx new file mode 100644 index 000000000..bd1712cdd --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-columns-list.tsx @@ -0,0 +1,147 @@ +import { observer } from "mobx-react-lite"; +// components +import { SpreadsheetColumn } from "components/issues"; +// types +import { + IIssue, + IIssueDisplayFilterOptions, + IIssueDisplayProperties, + IIssueLabels, + IStateResponse, + IUserLite, +} from "types"; + +type Props = { + displayFilters: IIssueDisplayFilterOptions; + displayProperties: IIssueDisplayProperties; + disableUserActions: boolean; + expandedIssues: string[]; + handleDisplayFilterUpdate: (data: Partial) => void; + handleUpdateIssue: (issue: IIssue, data: Partial) => void; + issues: IIssue[] | undefined; + members?: IUserLite[] | undefined; + labels?: IIssueLabels[] | undefined; + states?: IStateResponse | undefined; +}; + +export const SpreadsheetColumnsList: React.FC = observer((props) => { + const { + disableUserActions, + displayFilters, + displayProperties, + expandedIssues, + handleDisplayFilterUpdate, + handleUpdateIssue, + issues, + members, + labels, + states, + } = props; + + return ( + <> + {displayProperties.state && ( + + )} + {displayProperties.priority && ( + + )} + {displayProperties.assignee && ( + + )} + {displayProperties.labels && ( + + )}{" "} + {displayProperties.start_date && ( + + )} + {displayProperties.due_date && ( + + )} + {displayProperties.estimate && ( + + )} + {displayProperties.created_on && ( + + )} + {displayProperties.updated_on && ( + + )} + + ); +}); diff --git a/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx new file mode 100644 index 000000000..a1e642bbc --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/spreadsheet-view.tsx @@ -0,0 +1,188 @@ +import React, { useEffect, useRef, useState } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +import { PlusIcon } from "lucide-react"; +// components +import { + SpreadsheetColumnsList, + // ListInlineCreateIssueForm, + SpreadsheetIssuesColumn, +} from "components/issues"; +import { CustomMenu } from "components/ui"; +import { Spinner } from "@plane/ui"; +// types +import { + IIssue, + IIssueDisplayFilterOptions, + IIssueDisplayProperties, + IIssueLabels, + IStateResponse, + IUserLite, +} from "types"; + +type Props = { + displayProperties: IIssueDisplayProperties; + displayFilters: IIssueDisplayFilterOptions; + handleDisplayFilterUpdate: (data: Partial) => void; + issues: IIssue[] | undefined; + members?: IUserLite[] | undefined; + labels?: IIssueLabels[] | undefined; + states?: IStateResponse | undefined; + handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; + handleUpdateIssue: (issue: IIssue, data: Partial) => void; + openIssuesListModal?: (() => void) | null; + disableUserActions: boolean; +}; + +export const SpreadsheetView: React.FC = observer((props) => { + const { + displayProperties, + displayFilters, + handleDisplayFilterUpdate, + issues, + members, + labels, + states, + handleIssueAction, + handleUpdateIssue, + openIssuesListModal, + disableUserActions, + } = props; + + const [expandedIssues, setExpandedIssues] = useState([]); + + const [isInlineCreateIssueFormOpen, setIsInlineCreateIssueFormOpen] = useState(false); + + const [isScrolled, setIsScrolled] = useState(false); + + const containerRef = useRef(null); + + const router = useRouter(); + const { cycleId, moduleId } = router.query; + + const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; + + const handleScroll = () => { + if (!containerRef.current) return; + + const scrollLeft = containerRef.current.scrollLeft; + setIsScrolled(scrollLeft > 0); + }; + + useEffect(() => { + const currentContainerRef = containerRef.current; + + if (currentContainerRef) currentContainerRef.addEventListener("scroll", handleScroll); + + return () => { + if (currentContainerRef) currentContainerRef.removeEventListener("scroll", handleScroll); + }; + }, []); + + return ( + <> +
+
+
+ {issues ? ( + <> +
+
+
+ {displayProperties.key && ( + ID + )} + Issue +
+ + {issues.map((issue: IIssue, index) => ( + + ))} +
+
+ + + + ) : ( +
+ +
+ )} +
+ +
+
+ {/* setIsInlineCreateIssueFormOpen(false)} + prePopulatedData={{ + ...(cycleId && { cycle: cycleId.toString() }), + ...(moduleId && { module: moduleId.toString() }), + }} + /> */} +
+ + {!disableUserActions && + !isInlineCreateIssueFormOpen && + (type === "issue" ? ( + + ) : ( + + + New Issue + + } + optionsClassName="left-5 !w-36" + noBorder + > + setIsInlineCreateIssueFormOpen(true)}> + Create new + + {openIssuesListModal && ( + Add an existing issue + )} + + ))} +
+
+
+ + ); +}); diff --git a/web/components/core/views/spreadsheet-view/start-date-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/start-date-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/start-date-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/start-date-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/start-date-column/spreadsheet-start-date-column.tsx b/web/components/issues/issue-layouts/spreadsheet/start-date-column/spreadsheet-start-date-column.tsx new file mode 100644 index 000000000..61087fa19 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/start-date-column/spreadsheet-start-date-column.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +// components +import { StartDateColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (formData: Partial) => void; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetStartDateColumn: React.FC = ({ issue, onChange, expandedIssues, disabled }) => { + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ start_date: val })} disabled={disabled} /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/start-date-column/start-date-column.tsx b/web/components/issues/issue-layouts/spreadsheet/start-date-column/start-date-column.tsx new file mode 100644 index 000000000..cdb33f909 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/start-date-column/start-date-column.tsx @@ -0,0 +1,22 @@ +import React from "react"; +// components +import { ViewStartDateSelect } from "components/issues"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: string | null) => void; + disabled: boolean; +}; + +export const StartDateColumn: React.FC = (props) => { + const { issue, onChange, disabled } = props; + return ( +
+ + + +
+ ); +}; diff --git a/web/components/core/views/spreadsheet-view/state-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/state-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/state-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/state-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/state-column/spreadsheet-state-column.tsx b/web/components/issues/issue-layouts/spreadsheet/state-column/spreadsheet-state-column.tsx new file mode 100644 index 000000000..8a30bbb8c --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/state-column/spreadsheet-state-column.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +// components +import { StateColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue, IStateResponse } from "types"; + +type Props = { + issue: IIssue; + onChange: (data: Partial) => void; + states: IStateResponse | undefined; + expandedIssues: string[]; + disabled: boolean; +}; + +export const SpreadsheetStateColumn: React.FC = (props) => { + const { issue, onChange, states, expandedIssues, disabled } = props; + + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ onChange({ state: data.id, state_detail: data })} + states={states} + disabled={disabled} + /> + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/state-column/state-column.tsx b/web/components/issues/issue-layouts/spreadsheet/state-column/state-column.tsx new file mode 100644 index 000000000..3e290448b --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/state-column/state-column.tsx @@ -0,0 +1,31 @@ +import React from "react"; +// components +import { StateSelect } from "components/states"; +// types +import { IIssue, IState, IStateResponse } from "types"; + +type Props = { + issue: IIssue; + onChange: (formData: IState) => void; + states: IStateResponse | undefined; + disabled: boolean; +}; + +export const StateColumn: React.FC = (props) => { + const { issue, onChange, states, disabled } = props; + + return ( +
+ + + +
+ ); +}; diff --git a/web/components/core/views/spreadsheet-view/updated-on-column/index.ts b/web/components/issues/issue-layouts/spreadsheet/updated-on-column/index.ts similarity index 100% rename from web/components/core/views/spreadsheet-view/updated-on-column/index.ts rename to web/components/issues/issue-layouts/spreadsheet/updated-on-column/index.ts diff --git a/web/components/issues/issue-layouts/spreadsheet/updated-on-column/spreadsheet-updated-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/updated-on-column/spreadsheet-updated-on-column.tsx new file mode 100644 index 000000000..4752ea088 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/updated-on-column/spreadsheet-updated-on-column.tsx @@ -0,0 +1,35 @@ +import React from "react"; + +// components +import { UpdatedOnColumn } from "components/issues"; +// hooks +import useSubIssue from "hooks/use-sub-issue"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + expandedIssues: string[]; +}; + +export const SpreadsheetUpdatedOnColumn: React.FC = (props) => { + const { issue, expandedIssues } = props; + + const isExpanded = expandedIssues.indexOf(issue.id) > -1; + + const { subIssues, isLoading } = useSubIssue(issue.project_detail.id, issue.id, isExpanded); + + return ( +
+ + + {isExpanded && + !isLoading && + subIssues && + subIssues.length > 0 && + subIssues.map((subIssue: IIssue) => ( + + ))} +
+ ); +}; diff --git a/web/components/issues/issue-layouts/spreadsheet/updated-on-column/updated-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/updated-on-column/updated-on-column.tsx new file mode 100644 index 000000000..ed4c0294f --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/updated-on-column/updated-on-column.tsx @@ -0,0 +1,22 @@ +// helpers +import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; +}; + +export const UpdatedOnColumn: React.FC = (props) => { + const { issue } = props; + + return ( +
+ +
+ {renderLongDetailDateFormat(issue.updated_at)} +
+
+
+ ); +}; diff --git a/web/components/issues/sub-issues/properties.tsx b/web/components/issues/sub-issues/properties.tsx index 4e54091d6..723787e7f 100644 --- a/web/components/issues/sub-issues/properties.tsx +++ b/web/components/issues/sub-issues/properties.tsx @@ -1,6 +1,8 @@ import React from "react"; -// swr +import { observer } from "mobx-react-lite"; import { mutate } from "swr"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // services import { IssueService } from "services/issue"; import { TrackEventService } from "services/track_event.service"; @@ -28,181 +30,153 @@ export interface IIssueProperty { const issueService = new IssueService(); const trackEventService = new TrackEventService(); -export const IssueProperty: React.FC = ({ - workspaceSlug, - projectId, - parentIssue, - issue, - user, - editable, -}) => { - const [properties] = useIssuesProperties(workspaceSlug, projectId); +export const IssueProperty: React.FC = observer( + ({ workspaceSlug, projectId, parentIssue, issue, user, editable }) => { + const [properties] = useIssuesProperties(workspaceSlug, projectId); - const handlePriorityChange = (data: any) => { - partialUpdateIssue({ priority: data }); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_PRIORITY", - user as IUser - ); - }; + const { project: projectStore } = useMobxStore(); - const handleStateChange = (data: string, states: IState[] | undefined) => { - const oldState = states?.find((s) => s.id === issue.state); - const newState = states?.find((s) => s.id === data); - - partialUpdateIssue({ - state: data, - state_detail: newState, - }); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_STATE", - user as IUser - ); - if (oldState?.group !== "completed" && newState?.group !== "completed") { - trackEventService.trackIssueMarkedAsDoneEvent( + const handlePriorityChange = (data: any) => { + partialUpdateIssue({ priority: data }); + trackEventService.trackIssuePartialPropertyUpdateEvent( { - workspaceSlug: issue.workspace_detail.slug, - workspaceId: issue.workspace_detail.id, + workspaceSlug, + workspaceId: issue.workspace, projectId: issue.project_detail.id, projectIdentifier: issue.project_detail.identifier, projectName: issue.project_detail.name, issueId: issue.id, }, + "ISSUE_PROPERTY_UPDATE_PRIORITY", user as IUser ); - } - }; + }; - const handleAssigneeChange = (data: any) => { - let newData = issue.assignees ?? []; + const handleStateChange = (data: IState) => { + partialUpdateIssue({ + state: data.id, + state_detail: data, + }); + trackEventService.trackIssuePartialPropertyUpdateEvent( + { + workspaceSlug, + workspaceId: issue.workspace, + projectId: issue.project_detail.id, + projectIdentifier: issue.project_detail.identifier, + projectName: issue.project_detail.name, + issueId: issue.id, + }, + "ISSUE_PROPERTY_UPDATE_STATE", + user as IUser + ); + }; - if (newData && newData.length > 0) { - if (newData.includes(data)) newData = newData.splice(newData.indexOf(data), 1); - else newData = [...newData, data]; - } else newData = [...newData, data]; + const handleAssigneeChange = (data: string[]) => { + partialUpdateIssue({ assignees_list: data, assignees: data }); - partialUpdateIssue({ assignees_list: data, assignees: data }); + trackEventService.trackIssuePartialPropertyUpdateEvent( + { + workspaceSlug, + workspaceId: issue.workspace, + projectId: issue.project_detail.id, + projectIdentifier: issue.project_detail.identifier, + projectName: issue.project_detail.name, + issueId: issue.id, + }, + "ISSUE_PROPERTY_UPDATE_ASSIGNEE", + user as IUser + ); + }; - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_ASSIGNEE", - user as IUser - ); - }; + const partialUpdateIssue = async (data: Partial) => { + mutate( + workspaceSlug && parentIssue ? SUB_ISSUES(parentIssue.id) : null, + (elements: any) => { + const _elements = { ...elements }; + const _issues = _elements.sub_issues.map((element: IIssue) => + element.id === issue.id ? { ...element, ...data } : element + ); + _elements["sub_issues"] = [..._issues]; + return _elements; + }, + false + ); - const partialUpdateIssue = async (data: Partial) => { - mutate( - workspaceSlug && parentIssue ? SUB_ISSUES(parentIssue.id) : null, - (elements: any) => { - const _elements = { ...elements }; - const _issues = _elements.sub_issues.map((element: IIssue) => - element.id === issue.id ? { ...element, ...data } : element - ); - _elements["sub_issues"] = [..._issues]; - return _elements; - }, - false - ); + const issueResponse = await issueService.patchIssue(workspaceSlug as string, issue.project, issue.id, data, user); - const issueResponse = await issueService.patchIssue(workspaceSlug as string, issue.project, issue.id, data, user); + mutate( + SUB_ISSUES(parentIssue.id), + (elements: any) => { + const _elements = elements.sub_issues.map((element: IIssue) => + element.id === issue.id ? issueResponse : element + ); + elements["sub_issues"] = _elements; + return elements; + }, + true + ); + }; - mutate( - SUB_ISSUES(parentIssue.id), - (elements: any) => { - const _elements = elements.sub_issues.map((element: IIssue) => - element.id === issue.id ? issueResponse : element - ); - elements["sub_issues"] = _elements; - return elements; - }, - true - ); - }; - - return ( -
- {properties.priority && ( -
- -
- )} - - {properties.state && ( -
- -
- )} - - {properties.start_date && issue.start_date && ( -
- -
- )} - - {properties.due_date && issue.target_date && ( -
- {user && ( - + {properties.priority && ( +
+ - )} -
- )} +
+ )} - {properties.assignee && ( -
- -
- )} -
- ); -}; + {properties.state && ( +
+ handleStateChange(data)} + hideDropdownArrow + disabled={!editable} + /> +
+ )} + + {properties.start_date && issue.start_date && ( +
+ partialUpdateIssue({ start_date: val })} + disabled={!editable} + /> +
+ )} + + {properties.due_date && issue.target_date && ( +
+ {user && ( + partialUpdateIssue({ target_date: val })} + disabled={!editable} + /> + )} +
+ )} + + {properties.assignee && ( +
+ handleAssigneeChange(val)} + members={projectStore.members ? (projectStore.members[issue.project] ?? []).map((m) => m.member) : []} + hideDropdownArrow + disabled={!editable} + multiple + /> +
+ )} +
+ ); + } +); diff --git a/web/components/issues/view-select/due-date.tsx b/web/components/issues/view-select/due-date.tsx index 978859478..c2b94311d 100644 --- a/web/components/issues/view-select/due-date.tsx +++ b/web/components/issues/view-select/due-date.tsx @@ -1,43 +1,30 @@ -import { useRouter } from "next/router"; // ui import { CustomDatePicker } from "components/ui"; import { Tooltip } from "@plane/ui"; // helpers import { findHowManyDaysLeft, renderShortDateWithYearFormat } from "helpers/date-time.helper"; -// services -import { TrackEventService } from "services/track_event.service"; // types -import { IUser, IIssue } from "types"; -import useIssuesView from "hooks/use-issues-view"; +import { IIssue } from "types"; type Props = { issue: IIssue; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; + onChange: (date: string | null) => void; handleOnOpen?: () => void; handleOnClose?: () => void; tooltipPosition?: "top" | "bottom"; noBorder?: boolean; - user: IUser; - isNotAllowed: boolean; + disabled: boolean; }; -const trackEventService = new TrackEventService(); - export const ViewDueDateSelect: React.FC = ({ issue, - partialUpdateIssue, + onChange, handleOnOpen, handleOnClose, tooltipPosition = "top", noBorder = false, - user, - isNotAllowed, + disabled, }) => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { displayFilters } = useIssuesView(); - const minDate = issue.start_date ? new Date(issue.start_date) : null; minDate?.setDate(minDate.getDate()); @@ -59,34 +46,13 @@ export const ViewDueDateSelect: React.FC = ({ { - partialUpdateIssue( - { - target_date: val, - }, - issue - ); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_DUE_DATE", - user - ); - }} - className={`${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${ - displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100" - }`} + onChange={onChange} + className={`bg-transparent ${issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"}`} minDate={minDate ?? undefined} noBorder={noBorder} handleOnOpen={handleOnOpen} handleOnClose={handleOnClose} - disabled={isNotAllowed} + disabled={disabled} /> diff --git a/web/components/issues/view-select/estimate.tsx b/web/components/issues/view-select/estimate.tsx index 8bf702ada..34f6539c7 100644 --- a/web/components/issues/view-select/estimate.tsx +++ b/web/components/issues/view-select/estimate.tsx @@ -1,7 +1,4 @@ import React from "react"; -import { useRouter } from "next/router"; -// services -import { TrackEventService } from "services/track_event.service"; // hooks import useEstimateOption from "hooks/use-estimate-option"; // ui @@ -10,34 +7,23 @@ import { Tooltip } from "@plane/ui"; // icons import { Triangle } from "lucide-react"; // types -import { IUser, IIssue } from "types"; +import { IIssue } from "types"; type Props = { issue: IIssue; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; - position?: "left" | "right"; + onChange: (data: number) => void; tooltipPosition?: "top" | "bottom"; - selfPositioned?: boolean; customButton?: boolean; - user: IUser | undefined; - isNotAllowed: boolean; + disabled: boolean; }; -const trackEventService = new TrackEventService(); - export const ViewEstimateSelect: React.FC = ({ issue, - partialUpdateIssue, - // position = "left", + onChange, tooltipPosition = "top", - // selfPositioned = false, customButton = false, - user, - isNotAllowed, + disabled, }) => { - const router = useRouter(); - const { workspaceSlug } = router.query; - const { isEstimateActive, estimatePoints } = useEstimateOption(issue.estimate_point); const estimateValue = estimatePoints?.find((e) => e.key === issue.estimate_point)?.value; @@ -45,7 +31,7 @@ export const ViewEstimateSelect: React.FC = ({ const estimateLabels = (
- + {estimateValue ?? "None"}
@@ -56,31 +42,17 @@ export const ViewEstimateSelect: React.FC = ({ return ( { - partialUpdateIssue({ estimate_point: val }, issue); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_ESTIMATE", - user as IUser - ); - }} + onChange={onChange} {...(customButton ? { customButton: estimateLabels } : { label: estimateLabels })} maxHeight="md" noChevron - disabled={isNotAllowed} + disabled={disabled} width="w-full min-w-[8rem]" > <> - + None @@ -88,9 +60,7 @@ export const ViewEstimateSelect: React.FC = ({ {estimatePoints?.map((estimate) => ( <> - - - + {estimate.value} diff --git a/web/components/issues/view-select/start-date.tsx b/web/components/issues/view-select/start-date.tsx index 147fcc0be..7fd61cb44 100644 --- a/web/components/issues/view-select/start-date.tsx +++ b/web/components/issues/view-select/start-date.tsx @@ -1,44 +1,30 @@ -import { FC } from "react"; -import { useRouter } from "next/router"; // ui import { CustomDatePicker } from "components/ui"; import { Tooltip } from "@plane/ui"; // helpers import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; -// services -import { TrackEventService } from "services/track_event.service"; // types -import { IUser, IIssue } from "types"; -import useIssuesView from "hooks/use-issues-view"; +import { IIssue } from "types"; type Props = { issue: IIssue; - partialUpdateIssue: (formData: Partial, issue: IIssue) => void; + onChange: (date: string | null) => void; handleOnOpen?: () => void; handleOnClose?: () => void; tooltipPosition?: "top" | "bottom"; noBorder?: boolean; - user: IUser | undefined; - isNotAllowed: boolean; + disabled: boolean; }; -const trackEventService = new TrackEventService(); - -export const ViewStartDateSelect: FC = ({ +export const ViewStartDateSelect: React.FC = ({ issue, - partialUpdateIssue, + onChange, handleOnOpen, handleOnClose, tooltipPosition = "top", noBorder = false, - user, - isNotAllowed, + disabled, }) => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { displayFilters } = useIssuesView(); - const maxDate = issue.target_date ? new Date(issue.target_date) : null; maxDate?.setDate(maxDate.getDate()); @@ -52,34 +38,13 @@ export const ViewStartDateSelect: FC = ({ { - partialUpdateIssue( - { - start_date: val, - }, - issue - ); - trackEventService.trackIssuePartialPropertyUpdateEvent( - { - workspaceSlug, - workspaceId: issue.workspace, - projectId: issue.project_detail.id, - projectIdentifier: issue.project_detail.identifier, - projectName: issue.project_detail.name, - issueId: issue.id, - }, - "ISSUE_PROPERTY_UPDATE_DUE_DATE", - user as IUser - ); - }} - className={`${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"} ${ - displayFilters.layout === "kanban" ? "bg-custom-background-90" : "bg-custom-background-100" - }`} + onChange={onChange} + className={`bg-transparent ${issue?.start_date ? "w-[6.5rem]" : "w-[5rem] text-center"}`} maxDate={maxDate ?? undefined} noBorder={noBorder} handleOnOpen={handleOnOpen} handleOnClose={handleOnClose} - disabled={isNotAllowed} + disabled={disabled} /> diff --git a/web/components/project/label-select.tsx b/web/components/project/label-select.tsx index 353937d7a..f715793cb 100644 --- a/web/components/project/label-select.tsx +++ b/web/components/project/label-select.tsx @@ -1,31 +1,20 @@ import React, { useState } from "react"; - -import useSWR from "swr"; - -import { useRouter } from "next/router"; - -// react-popper import { usePopper } from "react-popper"; -// services -import { IssueLabelService } from "services/issue"; -// headless ui -import { Combobox } from "@headlessui/react"; -// component -import { CreateLabelModal } from "components/labels"; -// icons -import { Check, ChevronDown, PlusIcon, Search } from "lucide-react"; -// types -import { Tooltip } from "components/ui"; -import { IUser, IIssueLabels } from "types"; import { Placement } from "@popperjs/core"; -// constants -import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys"; +import { Combobox } from "@headlessui/react"; +import { Check, ChevronDown, PlusIcon, Search } from "lucide-react"; + +// components +import { CreateLabelModal } from "components/labels"; +// ui +import { Tooltip } from "components/ui"; +// types +import { IIssueLabels } from "types"; type Props = { value: string[]; - projectId: string; - onChange: (data: any) => void; - labelsDetails: any[]; + onChange: (data: string[]) => void; + labels: IIssueLabels[]; className?: string; buttonClassName?: string; optionsClassName?: string; @@ -33,17 +22,12 @@ type Props = { placement?: Placement; hideDropdownArrow?: boolean; disabled?: boolean; - user: IUser | undefined; }; -// services -const issueLabelService = new IssueLabelService(); - export const LabelSelect: React.FC = ({ value, - projectId, onChange, - labelsDetails, + labels, className = "", buttonClassName = "", optionsClassName = "", @@ -51,31 +35,19 @@ export const LabelSelect: React.FC = ({ placement, hideDropdownArrow = false, disabled = false, - user, }) => { const [query, setQuery] = useState(""); - const [fetchStates, setFetchStates] = useState(false); const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const [labelModal, setLabelModal] = useState(false); - const router = useRouter(); - const { workspaceSlug } = router.query; - const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "bottom-start", }); - const { data: issueLabels } = useSWR( - projectId && fetchStates ? PROJECT_ISSUE_LABELS(projectId) : null, - workspaceSlug && projectId && fetchStates - ? () => issueLabelService.getProjectIssueLabels(workspaceSlug.toString(), projectId) - : null - ); - - const options = issueLabels?.map((label) => ({ + const options = labels?.map((label) => ({ value: label.id, query: label.name, content: ( @@ -94,48 +66,6 @@ export const LabelSelect: React.FC = ({ const filteredOptions = query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); - const label = ( -
- {labelsDetails.length > 0 ? ( - labelsDetails.length <= maxRender ? ( - <> - {labelsDetails.map((label) => ( -
-
- - {label.name} -
-
- ))} - - ) : ( -
- l.name).join(", ")} - > -
- - {`${value.length} Labels`} -
-
-
- ) - ) : ( - "" - )} -
- ); - const footerOption = ( - - - -
-
- - setQuery(e.target.value)} - placeholder="Search" - displayValue={(assigned: any) => assigned?.name} - /> + +
+ {!hideDropdownArrow && !disabled &&