From 53e7da08e489e5781ec453920b356af1ae01c452 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Wed, 8 Nov 2023 12:28:02 +0530 Subject: [PATCH 01/19] fix: quick add not working for labels & assignee (#2689) * fix: quick add not working for labels & assignee * fix: build error on spreadsheet view --- .../calendar/inline-create-issue-form.tsx | 40 +-------- .../gantt/inline-create-issue-form.tsx | 4 +- .../kanban/inline-create-issue-form.tsx | 9 +- .../list/inline-create-issue-form.tsx | 12 +-- .../spreadsheet/inline-create-issue-form.tsx | 12 +-- web/constants/issue.ts | 73 --------------- web/helpers/issue.helper.ts | 90 ++++++++++++++++++- 7 files changed, 106 insertions(+), 134 deletions(-) diff --git a/web/components/issues/issue-layouts/calendar/inline-create-issue-form.tsx b/web/components/issues/issue-layouts/calendar/inline-create-issue-form.tsx index deef5f1bc..ddab04b0a 100644 --- a/web/components/issues/issue-layouts/calendar/inline-create-issue-form.tsx +++ b/web/components/issues/issue-layouts/calendar/inline-create-issue-form.tsx @@ -1,57 +1,27 @@ import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; - -// store import { observer } from "mobx-react-lite"; +// store import { useMobxStore } from "lib/mobx/store-provider"; - // hooks import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; - -// constants -import { createIssuePayload } from "constants/issue"; - +// helpers +import { createIssuePayload } from "helpers/issue.helper"; // icons import { PlusIcon } from "lucide-react"; - // types import { IIssue } from "types"; type Props = { groupId?: string; - dependencies?: any[]; prePopulatedData?: Partial; onSuccess?: (data: IIssue) => Promise | void; }; -const useCheckIfThereIsSpaceOnRight = (ref: React.RefObject, deps: any[]) => { - const [isThereSpaceOnRight, setIsThereSpaceOnRight] = useState(true); - - const router = useRouter(); - const { moduleId, cycleId, viewId } = router.query; - - const container = document.getElementById(`calendar-view-${cycleId ?? moduleId ?? viewId}`); - - useEffect(() => { - if (!ref.current) return; - - const { right } = ref.current.getBoundingClientRect(); - - const width = right; - - const innerWidth = container?.getBoundingClientRect().width ?? window.innerWidth; - - if (width > innerWidth) setIsThereSpaceOnRight(false); - else setIsThereSpaceOnRight(true); - }, [ref, deps, container]); - - return isThereSpaceOnRight; -}; - const defaultValues: Partial = { name: "", }; @@ -80,7 +50,7 @@ const Inputs = (props: any) => { }; export const CalendarInlineCreateIssueForm: React.FC = observer((props) => { - const { prePopulatedData, dependencies = [], groupId } = props; + const { prePopulatedData, groupId } = props; // router const router = useRouter(); @@ -135,8 +105,6 @@ export const CalendarInlineCreateIssueForm: React.FC = observer((props) = }); }, [errors, setToastAlert]); - const isSpaceOnRight = useCheckIfThereIsSpaceOnRight(ref, dependencies); - const onSubmitHandler = async (formData: IIssue) => { if (isSubmitting || !workspaceSlug || !projectId) return; diff --git a/web/components/issues/issue-layouts/gantt/inline-create-issue-form.tsx b/web/components/issues/issue-layouts/gantt/inline-create-issue-form.tsx index 8ce451e2b..0c87e6950 100644 --- a/web/components/issues/issue-layouts/gantt/inline-create-issue-form.tsx +++ b/web/components/issues/issue-layouts/gantt/inline-create-issue-form.tsx @@ -14,8 +14,8 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; import { renderDateFormat } from "helpers/date-time.helper"; // types import { IIssue } from "types"; -// constants -import { createIssuePayload } from "constants/issue"; +// helpers +import { createIssuePayload } from "helpers/issue.helper"; type Props = { prePopulatedData?: Partial; diff --git a/web/components/issues/issue-layouts/kanban/inline-create-issue-form.tsx b/web/components/issues/issue-layouts/kanban/inline-create-issue-form.tsx index 750606ebb..ad7b9362e 100644 --- a/web/components/issues/issue-layouts/kanban/inline-create-issue-form.tsx +++ b/web/components/issues/issue-layouts/kanban/inline-create-issue-form.tsx @@ -2,19 +2,16 @@ import { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; import { PlusIcon } from "lucide-react"; -// store import { observer } from "mobx-react-lite"; +// store import { useMobxStore } from "lib/mobx/store-provider"; - // hooks import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; - -// constants -import { createIssuePayload } from "constants/issue"; - +// helpers +import { createIssuePayload } from "helpers/issue.helper"; // types import { IIssue } from "types"; diff --git a/web/components/issues/issue-layouts/list/inline-create-issue-form.tsx b/web/components/issues/issue-layouts/list/inline-create-issue-form.tsx index effc96acb..813e25d0a 100644 --- a/web/components/issues/issue-layouts/list/inline-create-issue-form.tsx +++ b/web/components/issues/issue-layouts/list/inline-create-issue-form.tsx @@ -1,23 +1,19 @@ import { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; - +import { observer } from "mobx-react-lite"; +import { PlusIcon } from "lucide-react"; // hooks import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; - // store -import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; - -// constants -import { createIssuePayload } from "constants/issue"; - +// helpers +import { createIssuePayload } from "helpers/issue.helper"; // types import { IIssue } from "types"; -import { PlusIcon } from "lucide-react"; type Props = { groupId?: string; diff --git a/web/components/issues/issue-layouts/spreadsheet/inline-create-issue-form.tsx b/web/components/issues/issue-layouts/spreadsheet/inline-create-issue-form.tsx index 269ad5538..126d44498 100644 --- a/web/components/issues/issue-layouts/spreadsheet/inline-create-issue-form.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/inline-create-issue-form.tsx @@ -1,23 +1,19 @@ import { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; - +import { observer } from "mobx-react-lite"; +import { PlusIcon } from "lucide-react"; // hooks import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; - // store -import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; - -// constants -import { createIssuePayload } from "constants/issue"; - +// helpers +import { createIssuePayload } from "helpers/issue.helper"; // types import { IIssue } from "types"; -import { PlusIcon } from "lucide-react"; type Props = { groupId?: string; diff --git a/web/constants/issue.ts b/web/constants/issue.ts index 144cad190..5ba0f48a0 100644 --- a/web/constants/issue.ts +++ b/web/constants/issue.ts @@ -1,4 +1,3 @@ -import { v4 as uuidv4 } from "uuid"; // icons import { Calendar, GanttChartSquare, Kanban, List, Sheet } from "lucide-react"; // types @@ -12,9 +11,6 @@ import { TIssuePriorities, TIssueTypeFilters, TStateGroups, - IIssue, - IProject, - IWorkspace, } from "types"; export const ISSUE_PRIORITIES: { @@ -420,72 +416,3 @@ export const groupReactionEmojis = (reactions: any) => { return _groupedEmojis; }; - -/** - * - * @param workspaceDetail workspace detail to be added in the issue payload - * @param projectDetail project detail to be added in the issue payload - * @param formData partial issue data from the form. This will override the default values - * @returns full issue payload with some default values - */ - -export const createIssuePayload: ( - workspaceDetail: IWorkspace, - projectDetail: IProject, - formData: Partial -) => IIssue = (workspaceDetail: IWorkspace, projectDetail: IProject, formData: Partial) => { - const payload = { - archived_at: null, - assignees: [], - assignee_details: [], - attachment_count: 0, - attachments: [], - issue_relations: [], - related_issues: [], - bridge_id: null, - completed_at: new Date(), - created_at: "", - created_by: "", - cycle: null, - cycle_id: null, - cycle_detail: null, - description: {}, - description_html: "", - description_stripped: "", - estimate_point: null, - issue_cycle: null, - issue_link: [], - issue_module: null, - labels: [], - label_details: [], - is_draft: false, - links_list: [], - link_count: 0, - module: null, - module_id: null, - name: "", - parent: null, - parent_detail: null, - priority: "none", - project: projectDetail.id, - project_detail: projectDetail, - sequence_id: 0, - sort_order: 0, - sprints: null, - start_date: null, - state: projectDetail.default_state, - state_detail: {} as any, - sub_issues_count: 0, - target_date: null, - updated_at: "", - updated_by: "", - workspace: workspaceDetail.id, - workspace_detail: workspaceDetail, - id: uuidv4(), - tempId: uuidv4(), - // to be overridden by the form data - ...formData, - } as IIssue; - - return payload; -}; diff --git a/web/helpers/issue.helper.ts b/web/helpers/issue.helper.ts index 6f4381dec..39424c76e 100644 --- a/web/helpers/issue.helper.ts +++ b/web/helpers/issue.helper.ts @@ -1,7 +1,16 @@ +import { v4 as uuidv4 } from "uuid"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types -import { IIssue, TIssueGroupByOptions, TIssueLayouts, TIssueOrderByOptions, TIssueParams } from "types"; +import { + IIssue, + TIssueGroupByOptions, + TIssueLayouts, + TIssueOrderByOptions, + TIssueParams, + IProject, + IWorkspace, +} from "types"; // constants import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue"; @@ -109,3 +118,82 @@ export const handleIssueQueryParamsByLayout = ( return queryParams; }; + +/** + * + * @description create a full issue payload with some default values. This function also parse the form field + * like assignees, labels, etc. and add them to the payload + * @param workspaceDetail workspace detail to be added in the issue payload + * @param projectDetail project detail to be added in the issue payload + * @param formData partial issue data from the form. This will override the default values + * @returns full issue payload with some default values + */ + +export const createIssuePayload: ( + workspaceDetail: IWorkspace, + projectDetail: IProject, + formData: Partial +) => IIssue = (workspaceDetail: IWorkspace, projectDetail: IProject, formData: Partial) => { + const payload = { + archived_at: null, + assignee_details: [], + attachment_count: 0, + attachments: [], + issue_relations: [], + related_issues: [], + bridge_id: null, + completed_at: new Date(), + created_at: "", + created_by: "", + cycle: null, + cycle_id: null, + cycle_detail: null, + description: {}, + description_html: "", + description_stripped: "", + estimate_point: null, + issue_cycle: null, + issue_link: [], + issue_module: null, + label_details: [], + is_draft: false, + links_list: [], + link_count: 0, + module: null, + module_id: null, + name: "", + parent: null, + parent_detail: null, + priority: "none", + project: projectDetail.id, + project_detail: projectDetail, + sequence_id: 0, + sort_order: 0, + sprints: null, + start_date: null, + state: projectDetail.default_state, + state_detail: {} as any, + sub_issues_count: 0, + target_date: null, + updated_at: "", + updated_by: "", + workspace: workspaceDetail.id, + workspace_detail: workspaceDetail, + id: uuidv4(), + tempId: uuidv4(), + // to be overridden by the form data + ...formData, + assignees: Array.isArray(formData.assignees) + ? formData.assignees + : formData.assignees && formData.assignees !== "none" && formData.assignees !== null + ? [formData.assignees] + : [], + labels: Array.isArray(formData.labels) + ? formData.labels + : formData.labels && formData.labels !== "none" && formData.labels !== null + ? [formData.labels] + : [], + } as IIssue; + + return payload; +}; From 5a84ed279d5beab1c66397fbbcc220990eed1886 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:51:55 +0530 Subject: [PATCH 02/19] refactor: replace keyboard events with command palette store (#2688) --- .../command-palette/command-modal.tsx | 55 +++++++------------ .../cycles/active-cycle-details.tsx | 18 +++--- web/components/cycles/cycles-board.tsx | 20 +++---- web/components/cycles/cycles-list.tsx | 17 +++--- web/components/cycles/sidebar.tsx | 6 +- web/components/headers/cycle-issues.tsx | 14 +---- web/components/headers/cycles.tsx | 19 +++---- web/components/headers/module-issues.tsx | 13 +---- web/components/headers/modules-list.tsx | 7 +-- web/components/headers/page-details.tsx | 8 +-- web/components/headers/pages.tsx | 7 +-- web/components/headers/project-issues.tsx | 18 +++--- web/components/headers/projects.tsx | 11 +--- .../integration/jira/give-details.tsx | 12 ++-- .../issue-layouts/empty-states/cycle.tsx | 9 +-- .../empty-states/global-view.tsx | 40 +++++++------- .../issue-layouts/empty-states/module.tsx | 9 +-- .../empty-states/project-view.tsx | 40 +++++++------- .../issue-layouts/empty-states/project.tsx | 40 +++++++------- web/components/modules/modules-list-view.tsx | 9 +-- web/components/modules/sidebar.tsx | 6 +- .../page-views/workspace-dashboard.tsx | 14 +---- .../pages/pages-list/recent-pages-list.tsx | 32 +++++------ web/components/pages/pages-view.tsx | 36 ++++++------ web/components/project/card-list.tsx | 9 +-- web/components/project/sidebar-list.tsx | 17 ++---- web/components/views/views-list.tsx | 9 +-- web/components/workspace/help-section.tsx | 9 +-- .../workspace/sidebar-quick-action.tsx | 42 ++++++-------- web/layouts/auth-layout/project-wrapper.tsx | 8 +-- web/pages/[workspaceSlug]/analytics.tsx | 9 +-- 31 files changed, 219 insertions(+), 344 deletions(-) diff --git a/web/components/command-palette/command-modal.tsx b/web/components/command-palette/command-modal.tsx index 2d65bd58a..3acfb71a6 100644 --- a/web/components/command-palette/command-modal.tsx +++ b/web/components/command-palette/command-modal.tsx @@ -3,6 +3,7 @@ import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; import { Command } from "cmdk"; import { Dialog, Transition } from "@headlessui/react"; +import { observer } from "mobx-react-lite"; import { FileText, FolderPlus, @@ -16,12 +17,13 @@ import { UserMinus2, UserPlus2, } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // services import { WorkspaceService } from "services/workspace.service"; import { IssueService } from "services/issue"; // hooks import useDebounce from "hooks/use-debounce"; -import useUser from "hooks/use-user"; import useToast from "hooks/use-toast"; // components import { @@ -61,11 +63,8 @@ type Props = { const workspaceService = new WorkspaceService(); const issueService = new IssueService(); -export const CommandModal: React.FC = (props) => { +export const CommandModal: React.FC = observer((props) => { const { deleteIssue, isPaletteOpen, closePalette } = props; - // router - const router = useRouter(); - const { workspaceSlug, projectId, issueId } = router.query; // states const [placeholder, setPlaceholder] = useState("Type a command or search..."); const [resultsCount, setResultsCount] = useState(0); @@ -86,14 +85,19 @@ export const CommandModal: React.FC = (props) => { const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false); const [pages, setPages] = useState([]); + const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore(); + const user = userStore.currentUser ?? undefined; + + // router + const router = useRouter(); + const { workspaceSlug, projectId, issueId } = router.query; + const page = pages[pages.length - 1]; const debouncedSearchTerm = useDebounce(searchTerm, 500); const { setToastAlert } = useToast(); - const { user } = useUser(); - const { data: issueDetails } = useSWR( workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, workspaceSlug && projectId && issueId @@ -468,10 +472,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "c", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreateIssueModal(true); }} className="focus:bg-custom-background-80" > @@ -488,10 +489,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "p", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreateProjectModal(true); }} className="focus:outline-none" > @@ -510,10 +508,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "q", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreateCycleModal(true); }} className="focus:outline-none" > @@ -528,10 +523,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "m", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreateModuleModal(true); }} className="focus:outline-none" > @@ -546,10 +538,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "v", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreateViewModal(true); }} className="focus:outline-none" > @@ -564,10 +553,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "d", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleCreatePageModal(true); }} className="focus:outline-none" > @@ -621,10 +607,7 @@ export const CommandModal: React.FC = (props) => { { closePalette(); - const e = new KeyboardEvent("keydown", { - key: "h", - }); - document.dispatchEvent(e); + commandPaletteStore.toggleShortcutModal(true); }} className="focus:outline-none" > @@ -762,4 +745,4 @@ export const CommandModal: React.FC = (props) => { ); -}; +}); diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index fc89ac3fb..c18399101 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -1,10 +1,12 @@ import { MouseEvent } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; import useSWR from "swr"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // hooks import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; // ui import { SingleProgressStats } from "components/core"; import { @@ -25,7 +27,6 @@ import { ActiveCycleProgressStats } from "components/cycles"; import { ViewIssueLabel } from "components/issues"; // icons import { AlarmClock, AlertTriangle, ArrowRight, CalendarDays, Star, Target } from "lucide-react"; - // helpers import { getDateRangeStatus, renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper"; import { truncateText } from "helpers/string.helper"; @@ -65,12 +66,12 @@ interface IActiveCycleDetails { projectId: string; } -export const ActiveCycleDetails: React.FC = (props) => { +export const ActiveCycleDetails: React.FC = observer((props) => { const router = useRouter(); const { workspaceSlug, projectId } = props; - const { cycle: cycleStore } = useMobxStore(); + const { cycle: cycleStore, commandPalette: commandPaletteStore } = useMobxStore(); const { setToastAlert } = useToast(); @@ -117,12 +118,7 @@ export const ActiveCycleDetails: React.FC = (props) => { @@ -485,4 +481,4 @@ export const ActiveCycleDetails: React.FC = (props) => { ); -}; +}); diff --git a/web/components/cycles/cycles-board.tsx b/web/components/cycles/cycles-board.tsx index 105d16128..61d21c2e0 100644 --- a/web/components/cycles/cycles-board.tsx +++ b/web/components/cycles/cycles-board.tsx @@ -1,8 +1,11 @@ import { FC } from "react"; -// types -import { ICycle } from "types"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // components import { CyclePeekOverview, CyclesBoardCard } from "components/cycles"; +// types +import { ICycle } from "types"; export interface ICyclesBoard { cycles: ICycle[]; @@ -12,9 +15,11 @@ export interface ICyclesBoard { peekCycle: string; } -export const CyclesBoard: FC = (props) => { +export const CyclesBoard: FC = observer((props) => { const { cycles, filter, workspaceSlug, projectId, peekCycle } = props; + const { commandPalette: commandPaletteStore } = useMobxStore(); + return ( <> {cycles.length > 0 ? ( @@ -53,12 +58,7 @@ export const CyclesBoard: FC = (props) => { @@ -67,4 +67,4 @@ export const CyclesBoard: FC = (props) => { )} ); -}; +}); diff --git a/web/components/cycles/cycles-list.tsx b/web/components/cycles/cycles-list.tsx index 03698f1d8..0cff682af 100644 --- a/web/components/cycles/cycles-list.tsx +++ b/web/components/cycles/cycles-list.tsx @@ -1,7 +1,9 @@ import { FC } from "react"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // components import { CyclePeekOverview, CyclesListItem } from "components/cycles"; - // ui import { Loader } from "@plane/ui"; // types @@ -14,9 +16,11 @@ export interface ICyclesList { projectId: string; } -export const CyclesList: FC = (props) => { +export const CyclesList: FC = observer((props) => { const { cycles, filter, workspaceSlug, projectId } = props; + const { commandPalette: commandPaletteStore } = useMobxStore(); + return ( <> {cycles ? ( @@ -53,12 +57,7 @@ export const CyclesList: FC = (props) => { @@ -75,4 +74,4 @@ export const CyclesList: FC = (props) => { )} ); -}; +}); diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index ea154d48b..1c7d0c58b 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -317,11 +317,11 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { {!isCompleted && ( - + setCycleDeleteModal(true)}> - - Delete + + Delete cycle diff --git a/web/components/headers/cycle-issues.tsx b/web/components/headers/cycle-issues.tsx index 4106f443b..42836b0ea 100644 --- a/web/components/headers/cycle-issues.tsx +++ b/web/components/headers/cycle-issues.tsx @@ -31,6 +31,7 @@ export const CycleIssuesHeader: React.FC = observer(() => { cycle: cycleStore, cycleIssueFilter: cycleIssueFilterStore, project: projectStore, + commandPalette: commandPaletteStore, } = useMobxStore(); const { currentProjectDetails } = projectStore; @@ -139,7 +140,6 @@ export const CycleIssuesHeader: React.FC = observer(() => { type="component" component={ @@ -148,6 +148,7 @@ export const CycleIssuesHeader: React.FC = observer(() => { } className="ml-1.5 flex-shrink-0" width="auto" + placement="bottom-start" > {cyclesList?.map((cycle) => ( { - ); -}; +}); diff --git a/web/components/headers/module-issues.tsx b/web/components/headers/module-issues.tsx index 42c01d531..1eec1cff4 100644 --- a/web/components/headers/module-issues.tsx +++ b/web/components/headers/module-issues.tsx @@ -31,6 +31,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => { module: moduleStore, moduleFilter: moduleFilterStore, project: projectStore, + commandPalette: commandPaletteStore, } = useMobxStore(); const activeLayout = issueFilterStore.userDisplayFilters.layout; const { currentProjectDetails } = projectStore; @@ -146,6 +147,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => { } className="ml-1.5 flex-shrink-0" width="auto" + placement="bottom-start" > {modulesList?.map((module) => ( { - diff --git a/web/components/headers/page-details.tsx b/web/components/headers/page-details.tsx index 8b9fa433d..a0bf29d05 100644 --- a/web/components/headers/page-details.tsx +++ b/web/components/headers/page-details.tsx @@ -24,10 +24,11 @@ const pageService = new PageService(); export const PageDetailsHeader: FC = observer((props) => { const { showButton = false } = props; + const router = useRouter(); const { workspaceSlug, pageId } = router.query; - const { project: projectStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); const { currentProjectDetails } = projectStore; const { data: pageDetails } = useSWR( @@ -78,10 +79,7 @@ export const PageDetailsHeader: FC = observer((props) => { variant="primary" prependIcon={} size="sm" - onClick={() => { - const e = new KeyboardEvent("keydown", { key: "d" }); - document.dispatchEvent(e); - }} + onClick={() => commandPaletteStore.toggleCreatePageModal(true)} > Create Page diff --git a/web/components/headers/pages.tsx b/web/components/headers/pages.tsx index 634dd0c38..0a3fd53f6 100644 --- a/web/components/headers/pages.tsx +++ b/web/components/headers/pages.tsx @@ -18,7 +18,7 @@ export const PagesHeader: FC = observer((props) => { const router = useRouter(); const { workspaceSlug } = router.query; - const { project: projectStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); const { currentProjectDetails } = projectStore; return ( @@ -56,10 +56,7 @@ export const PagesHeader: FC = observer((props) => { variant="primary" prependIcon={} size="sm" - onClick={() => { - const e = new KeyboardEvent("keydown", { key: "d" }); - document.dispatchEvent(e); - }} + onClick={() => commandPaletteStore.toggleCreatePageModal(true)} > Create Page diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx index f9bf6ec58..efe4dcf51 100644 --- a/web/components/headers/project-issues.tsx +++ b/web/components/headers/project-issues.tsx @@ -23,7 +23,12 @@ export const ProjectIssuesHeader: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { issueFilter: issueFilterStore, project: projectStore, inbox: inboxStore } = useMobxStore(); + const { + issueFilter: issueFilterStore, + project: projectStore, + inbox: inboxStore, + commandPalette: commandPaletteStore, + } = useMobxStore(); const activeLayout = issueFilterStore.userDisplayFilters.layout; @@ -198,16 +203,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => { - diff --git a/web/components/headers/projects.tsx b/web/components/headers/projects.tsx index 3c56f239e..fa51493ed 100644 --- a/web/components/headers/projects.tsx +++ b/web/components/headers/projects.tsx @@ -11,7 +11,7 @@ export const ProjectsHeader = observer(() => { const { workspaceSlug } = router.query; // store - const { project: projectStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : []; @@ -43,14 +43,7 @@ export const ProjectsHeader = observer(() => { )} - diff --git a/web/components/integration/jira/give-details.tsx b/web/components/integration/jira/give-details.tsx index 622517439..8a7c841de 100644 --- a/web/components/integration/jira/give-details.tsx +++ b/web/components/integration/jira/give-details.tsx @@ -16,15 +16,14 @@ export const JiraGetImportDetail: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug } = router.query; + const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); + const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; + const { control, formState: { errors }, } = useFormContext(); - const { project: projectStore } = useMobxStore(); - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - return (
@@ -190,10 +189,7 @@ export const JiraGetImportDetail: React.FC = observer(() => {
- + setModuleDeleteModal(true)}> - - Delete + + Delete module diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index 5ed28b00b..b7fcb7cb6 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -18,7 +18,8 @@ export const WorkspaceDashboardView = observer(() => { const router = useRouter(); const { workspaceSlug } = router.query; // store - const { user: userStore, project: projectStore } = useMobxStore(); + const { user: userStore, project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); + const user = userStore.currentUser; const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; const workspaceDashboardInfo = userStore.dashboardInfo; @@ -67,16 +68,7 @@ export const WorkspaceDashboardView = observer(() => {
Create a project

Manage your projects by creating issues, cycles, modules, views and pages.

-
diff --git a/web/components/pages/pages-list/recent-pages-list.tsx b/web/components/pages/pages-list/recent-pages-list.tsx index e4bc46c66..59bc8c842 100644 --- a/web/components/pages/pages-list/recent-pages-list.tsx +++ b/web/components/pages/pages-list/recent-pages-list.tsx @@ -1,19 +1,18 @@ import React from "react"; - import { useRouter } from "next/router"; - +import { observer } from "mobx-react-lite"; import useSWR from "swr"; - +import { Plus } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // services import { PageService } from "services/page.service"; // components import { PagesView } from "components/pages"; -// ui import { EmptyState } from "components/common"; +// ui import { Loader } from "@plane/ui"; -// icons -import { Plus } from "lucide-react"; -// images +// assets import emptyPage from "public/empty-state/page.svg"; // helpers import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; @@ -26,7 +25,11 @@ import { RECENT_PAGES_LIST } from "constants/fetch-keys"; // services const pageService = new PageService(); -export const RecentPagesList: React.FC = ({ viewType }) => { +export const RecentPagesList: React.FC = observer((props) => { + const { viewType } = props; + + const { commandPalette: commandPaletteStore } = useMobxStore(); + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -46,9 +49,7 @@ export const RecentPagesList: React.FC = ({ viewType }) => { return (
-

- {replaceUnderscoreIfSnakeCase(key)} -

+

{replaceUnderscoreIfSnakeCase(key)}

); @@ -61,12 +62,7 @@ export const RecentPagesList: React.FC = ({ viewType }) => { primaryButton={{ icon: , text: "New Page", - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "d", - }); - document.dispatchEvent(e); - }, + onClick: () => commandPaletteStore.toggleCreatePageModal(true), }} /> ) @@ -79,4 +75,4 @@ export const RecentPagesList: React.FC = ({ viewType }) => { )} ); -}; +}); diff --git a/web/components/pages/pages-view.tsx b/web/components/pages/pages-view.tsx index 699cbb582..5aca1de1b 100644 --- a/web/components/pages/pages-view.tsx +++ b/web/components/pages/pages-view.tsx @@ -1,21 +1,20 @@ import { useState } from "react"; - -import useSWR, { mutate } from "swr"; import { useRouter } from "next/router"; - +import { observer } from "mobx-react-lite"; +import useSWR, { mutate } from "swr"; +import { Plus } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // services import { PageService } from "services/page.service"; import { ProjectService } from "services/project"; // hooks import useToast from "hooks/use-toast"; -import useUserAuth from "hooks/use-user-auth"; // components import { CreateUpdatePageModal, DeletePageModal, SinglePageDetailedItem, SinglePageListItem } from "components/pages"; -// ui import { EmptyState } from "components/common"; +// ui import { Loader } from "@plane/ui"; -// icons -import { Plus } from "lucide-react"; // images import emptyPage from "public/empty-state/page.svg"; // types @@ -37,17 +36,19 @@ type Props = { const pageService = new PageService(); const projectService = new ProjectService(); -export const PagesView: React.FC = ({ pages, viewType }) => { - // router - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; +export const PagesView: React.FC = observer(({ pages, viewType }) => { // states const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false); const [selectedPageToUpdate, setSelectedPageToUpdate] = useState(null); const [deletePageModal, setDeletePageModal] = useState(false); const [selectedPageToDelete, setSelectedPageToDelete] = useState(null); - const { user } = useUserAuth(); + const { user: userStore, commandPalette: commandPaletteStore } = useMobxStore(); + const user = userStore.currentUser ?? undefined; + + // router + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); @@ -163,7 +164,7 @@ export const PagesView: React.FC = ({ pages, viewType }) => { }; const partialUpdatePage = (page: IPage, formData: Partial) => { - if (!workspaceSlug || !projectId) return; + if (!workspaceSlug || !projectId || !user) return; mutate( ALL_PAGES_LIST(projectId.toString()), @@ -264,12 +265,7 @@ export const PagesView: React.FC = ({ pages, viewType }) => { primaryButton={{ icon: , text: "New Page", - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "d", - }); - document.dispatchEvent(e); - }, + onClick: () => commandPaletteStore.toggleCreatePageModal(true), }} /> )} @@ -294,4 +290,4 @@ export const PagesView: React.FC = ({ pages, viewType }) => { )} ); -}; +}); diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 37432e367..0a090f636 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -18,7 +18,7 @@ export interface IProjectCardList { export const ProjectCardList: FC = observer((props) => { const { workspaceSlug } = props; // store - const { project: projectStore } = useMobxStore(); + const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; @@ -53,12 +53,7 @@ export const ProjectCardList: FC = observer((props) => { primaryButton={{ icon: , text: "New Project", - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "p", - }); - document.dispatchEvent(e); - }, + onClick: () => commandPaletteStore.toggleCreateProjectModal(true), }} /> )} diff --git a/web/components/project/sidebar-list.tsx b/web/components/project/sidebar-list.tsx index 98d3cdb02..cb501a697 100644 --- a/web/components/project/sidebar-list.tsx +++ b/web/components/project/sidebar-list.tsx @@ -19,11 +19,6 @@ import { IProject } from "types"; import { useMobxStore } from "lib/mobx/store-provider"; export const ProjectSidebarList: FC = observer(() => { - const { theme: themeStore, project: projectStore } = useMobxStore(); - // router - const router = useRouter(); - const { workspaceSlug } = router.query; - // states const [isFavoriteProjectCreate, setIsFavoriteProjectCreate] = useState(false); const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); @@ -31,6 +26,11 @@ export const ProjectSidebarList: FC = observer(() => { // refs const containerRef = useRef(null); + const { theme: themeStore, project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); + // router + const router = useRouter(); + const { workspaceSlug } = router.query; + // toast const { setToastAlert } = useToast(); @@ -254,12 +254,7 @@ export const ProjectSidebarList: FC = observer(() => { diff --git a/web/components/workspace/sidebar-quick-action.tsx b/web/components/workspace/sidebar-quick-action.tsx index b09496cc4..d801b2772 100644 --- a/web/components/workspace/sidebar-quick-action.tsx +++ b/web/components/workspace/sidebar-quick-action.tsx @@ -8,14 +8,18 @@ import useLocalStorage from "hooks/use-local-storage"; import { CreateUpdateDraftIssueModal } from "components/issues"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; +import { observer } from "mobx-react-lite"; -export const WorkspaceSidebarQuickAction = () => { - const store: any = useMobxStore(); - +export const WorkspaceSidebarQuickAction = observer(() => { + // states const [isDraftIssueModalOpen, setIsDraftIssueModalOpen] = useState(false); + const { theme: themeStore, commandPalette: commandPaletteStore } = useMobxStore(); + const { storedValue, clearValue } = useLocalStorage("draftedIssue", JSON.stringify({})); + const isSidebarCollapsed = themeStore.sidebarCollapsed; + return ( <> {
{ {storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && ( <> -
+
); -}; +}); diff --git a/web/layouts/auth-layout/project-wrapper.tsx b/web/layouts/auth-layout/project-wrapper.tsx index e3eca37ff..bc401a3d3 100644 --- a/web/layouts/auth-layout/project-wrapper.tsx +++ b/web/layouts/auth-layout/project-wrapper.tsx @@ -25,6 +25,7 @@ export const ProjectAuthWrapper: FC = observer((props) => { module: moduleStore, projectViews: projectViewsStore, inbox: inboxStore, + commandPalette: commandPaletteStore, } = useMobxStore(); // router const router = useRouter(); @@ -131,12 +132,7 @@ export const ProjectAuthWrapper: FC = observer((props) => { image={emptyProject} primaryButton={{ text: "Create Project", - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "p", - }); - document.dispatchEvent(e); - }, + onClick: () => commandPaletteStore.toggleCreateProjectModal(true), }} />
diff --git a/web/pages/[workspaceSlug]/analytics.tsx b/web/pages/[workspaceSlug]/analytics.tsx index 0faa835ad..1d76f7754 100644 --- a/web/pages/[workspaceSlug]/analytics.tsx +++ b/web/pages/[workspaceSlug]/analytics.tsx @@ -28,7 +28,7 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { const router = useRouter(); const { workspaceSlug } = router.query; // store - const { project: projectStore, user: userStore } = useMobxStore(); + const { project: projectStore, user: userStore, commandPalette: commandPaletteStore } = useMobxStore(); const user = userStore.currentUser; const projects = workspaceSlug ? projectStore.projects[workspaceSlug?.toString()] : null; @@ -96,12 +96,7 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { primaryButton={{ icon: , text: "New Project", - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "p", - }); - document.dispatchEvent(e); - }, + onClick: () => commandPaletteStore.toggleCreateProjectModal(true), }} /> From 621d551c4a0b7960586c7bdae0ac28aaa980c7e4 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:33:26 +0530 Subject: [PATCH 03/19] fix: project select validation (#2723) --- web/components/issues/form.tsx | 6 +++++- web/components/issues/select/project.tsx | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index b54ce1121..2c9fb4eab 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -272,9 +272,13 @@ export const IssueForm: FC = observer((props) => { ( + rules={{ + required: true, + }} + render={({ field: { value, onChange }, fieldState: { error } }) => ( { onChange(val); setActiveProject(val); diff --git a/web/components/issues/select/project.tsx b/web/components/issues/select/project.tsx index 08f245b14..ad55471ce 100644 --- a/web/components/issues/select/project.tsx +++ b/web/components/issues/select/project.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; +import type { FieldError } from "react-hook-form"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // popper js @@ -15,6 +16,7 @@ import { Check, Clipboard, Search } from "lucide-react"; export interface IssueProjectSelectProps { value: string; onChange: (value: string) => void; + error?: FieldError; } export const IssueProjectSelect: React.FC = observer((props) => { From da799b5a6309c1927f17bb3bbb223afe9ec34ece Mon Sep 17 00:00:00 2001 From: Ankush Deshmukh Date: Wed, 8 Nov 2023 17:34:09 +0530 Subject: [PATCH 04/19] Fix: Render bar chart axis labels in lighter color when dark theme applied (#2721) --- web/components/analytics/custom-analytics/graph/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/components/analytics/custom-analytics/graph/index.tsx b/web/components/analytics/custom-analytics/graph/index.tsx index 181eec8bd..5fc21a3ec 100644 --- a/web/components/analytics/custom-analytics/graph/index.tsx +++ b/web/components/analytics/custom-analytics/graph/index.tsx @@ -114,6 +114,7 @@ export const AnalyticsGraph: React.FC = ({ analytics, barGraphData, param y={datum.y} textAnchor="end" fontSize={10} + fill="rgb(var(--color-text-200))" className={`${barGraphData.data.length > 7 ? "-rotate-45" : ""}`} > {generateDisplayName(datum.value, analytics, params, "x_axis")} From df8bdfd5b9150c426a43adca7d96d1ae36a4aebb Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:34:42 +0530 Subject: [PATCH 05/19] fix: project automation settings flickering (#2680) * fix: cycle and module sidebar z-index * fix: project automation settings flickering --- .../automation/auto-archive-automation.tsx | 92 +++++---- .../automation/auto-close-automation.tsx | 193 +++++++++--------- web/components/cycles/cycle-peek-overview.tsx | 2 +- .../modules/module-peek-overview.tsx | 2 +- .../projects/[projectId]/cycles/[cycleId].tsx | 2 +- .../[projectId]/modules/[moduleId].tsx | 2 +- .../[projectId]/settings/automations.tsx | 20 +- 7 files changed, 156 insertions(+), 157 deletions(-) diff --git a/web/components/automation/auto-archive-automation.tsx b/web/components/automation/auto-archive-automation.tsx index 966892595..1083073da 100644 --- a/web/components/automation/auto-archive-automation.tsx +++ b/web/components/automation/auto-archive-automation.tsx @@ -1,7 +1,9 @@ import React, { useState } from "react"; - +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // component -import { CustomSelect, ToggleSwitch } from "@plane/ui"; +import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui"; import { SelectMonthModal } from "components/automation"; // icon import { ArchiveRestore } from "lucide-react"; @@ -11,15 +13,21 @@ import { PROJECT_AUTOMATION_MONTHS } from "constants/project"; import { IProject } from "types"; type Props = { - projectDetails: IProject | undefined; handleChange: (formData: Partial) => Promise; - disabled?: boolean; }; -export const AutoArchiveAutomation: React.FC = ({ projectDetails, handleChange, disabled = false }) => { +const initialValues: Partial = { archive_in: 1 }; + +export const AutoArchiveAutomation: React.FC = observer((props) => { + const { handleChange } = props; + // states const [monthModal, setmonthModal] = useState(false); - const initialValues: Partial = { archive_in: 1 }; + const { user: userStore, project: projectStore } = useMobxStore(); + + const projectDetails = projectStore.currentProjectDetails; + const userRole = userStore.currentProjectRole; + return ( <> = ({ projectDetails, handleC projectDetails?.archive_in === 0 ? handleChange({ archive_in: 1 }) : handleChange({ archive_in: 0 }) } size="sm" - disabled={disabled} + disabled={userRole !== 20} />
- {projectDetails?.archive_in !== 0 && ( -
-
-
Auto-archive issues that are closed for
-
- { - handleChange({ archive_in: val }); - }} - input - width="w-full" - disabled={disabled} - > - <> - {PROJECT_AUTOMATION_MONTHS.map((month) => ( - - {month.label} - - ))} + {projectDetails ? ( + projectDetails.archive_in !== 0 && ( +
+
+
Auto-archive issues that are closed for
+
+ { + handleChange({ archive_in: val }); + }} + input + width="w-full" + disabled={userRole !== 20} + > + <> + {PROJECT_AUTOMATION_MONTHS.map((month) => ( + + {month.label} + + ))} - - - + + + +
-
+ ) + ) : ( + + + )}
); -}; +}); diff --git a/web/components/automation/auto-close-automation.tsx b/web/components/automation/auto-close-automation.tsx index b0aad20cd..2ccebe8b2 100644 --- a/web/components/automation/auto-close-automation.tsx +++ b/web/components/automation/auto-close-automation.tsx @@ -1,41 +1,33 @@ import React, { useState } from "react"; -import useSWR from "swr"; -import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // component import { SelectMonthModal } from "components/automation"; -import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleCircleIcon } from "@plane/ui"; +import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleCircleIcon, Loader } from "@plane/ui"; // icons import { ArchiveX } from "lucide-react"; -// services -import { ProjectStateService } from "services/project"; -// constants -import { PROJECT_AUTOMATION_MONTHS } from "constants/project"; -import { STATES_LIST } from "constants/fetch-keys"; +// helpers +import { getStatesList } from "helpers/state.helper"; // types import { IProject } from "types"; -// helper -import { getStatesList } from "helpers/state.helper"; +// fetch keys +import { PROJECT_AUTOMATION_MONTHS } from "constants/project"; type Props = { - projectDetails: IProject | undefined; handleChange: (formData: Partial) => Promise; - disabled?: boolean; }; -const projectStateService = new ProjectStateService(); - -export const AutoCloseAutomation: React.FC = ({ projectDetails, handleChange, disabled = false }) => { +export const AutoCloseAutomation: React.FC = observer((props) => { + const { handleChange } = props; + // states const [monthModal, setmonthModal] = useState(false); - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { user: userStore, project: projectStore } = useMobxStore(); - const { data: stateGroups } = useSWR( - workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => projectStateService.getStates(workspaceSlug as string, projectId as string) - : null - ); + const userRole = userStore.currentProjectRole; + const projectDetails = projectStore.currentProjectDetails; + const stateGroups = projectStore.projectStatesByGroups ?? undefined; const states = getStatesList(stateGroups); const options = states @@ -72,8 +64,7 @@ export const AutoCloseAutomation: React.FC = ({ projectDetails, handleCha handleClose={() => setmonthModal(false)} handleChange={handleChange} /> - -
+
@@ -82,7 +73,7 @@ export const AutoCloseAutomation: React.FC = ({ projectDetails, handleCha

Auto-close issues

- Plane will automatically close issue that haven’t been completed or cancelled. + Plane will automatically close issue that haven{"'"}t been completed or cancelled.

@@ -94,87 +85,93 @@ export const AutoCloseAutomation: React.FC = ({ projectDetails, handleCha : handleChange({ close_in: 0, default_state: null }) } size="sm" - disabled={disabled} + disabled={userRole !== 20} />
- {projectDetails?.close_in !== 0 && ( -
-
-
-
Auto-close issues that are inactive for
-
- { - handleChange({ close_in: val }); - }} - input - width="w-full" - disabled={disabled} - > - <> - {PROJECT_AUTOMATION_MONTHS.map((month) => ( - - {month.label} - - ))} - - - + {projectDetails ? ( + projectDetails.close_in !== 0 && ( +
+
+
+
Auto-close issues that are inactive for
+
+ { + handleChange({ close_in: val }); + }} + input + width="w-full" + disabled={userRole !== 20} + > + <> + {PROJECT_AUTOMATION_MONTHS.map((month) => ( + + {month.label} + + ))} + + + +
-
-
-
Auto-close Status
-
- - {selectedOption ? ( - - ) : currentDefaultState ? ( - - ) : ( - - )} - {selectedOption?.name - ? selectedOption.name - : currentDefaultState?.name ?? State} -
- } - onChange={(val: string) => { - handleChange({ default_state: val }); - }} - options={options} - disabled={!multipleOptions} - width="w-full" - input - /> +
+
Auto-close Status
+
+ + {selectedOption ? ( + + ) : currentDefaultState ? ( + + ) : ( + + )} + {selectedOption?.name + ? selectedOption.name + : currentDefaultState?.name ?? State} +
+ } + onChange={(val: string) => { + handleChange({ default_state: val }); + }} + options={options} + disabled={!multipleOptions} + width="w-full" + input + /> +
-
+ ) + ) : ( + + + )}
); -}; +}); diff --git a/web/components/cycles/cycle-peek-overview.tsx b/web/components/cycles/cycle-peek-overview.tsx index fb30150ca..f1e0cf084 100644 --- a/web/components/cycles/cycle-peek-overview.tsx +++ b/web/components/cycles/cycle-peek-overview.tsx @@ -41,7 +41,7 @@ export const CyclePeekOverview: React.FC = observer(({ projectId, workspa {peekCycle && (
= observer(({ projectId, worksp {peekModule && (
{
{cycleId && !isSidebarCollapsed && (
{
{moduleId && !isSidebarCollapsed && (
{ const handleChange = async (formData: Partial) => { if (!workspaceSlug || !projectId || !projectDetails) return; - mutate( - PROJECT_DETAILS(projectId as string), - (prevData) => ({ ...(prevData as IProject), ...formData }), - false - ); - - mutate( - PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }), - (prevData) => (prevData ?? []).map((p) => (p.id === projectDetails.id ? { ...p, ...formData } : p)), - false - ); - await projectService .updateProject(workspaceSlug as string, projectId as string, formData, user) .then(() => {}) @@ -72,8 +60,8 @@ const AutomationSettingsPage: NextPageWithLayout = () => {

Automations

- - + + ); }; From 83e0c4ebbd4ea58199d770f964e3bfee8d5e4bc0 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:35:45 +0530 Subject: [PATCH 06/19] chore: remove active ids from the MobX stores if not present in the route (#2681) * chore: remove active ids if not present in the route * refactor: set active id logic --- web/lib/mobx/store-init.tsx | 13 +++++++------ web/store/cycle/cycles.store.ts | 4 ++-- web/store/global-view/global_views.store.ts | 4 ++-- web/store/inbox/inbox.store.ts | 4 ++-- web/store/module/modules.store.ts | 6 +++--- web/store/project-view/project_views.store.ts | 4 ++-- web/store/project/project.store.ts | 6 +++--- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/web/lib/mobx/store-init.tsx b/web/lib/mobx/store-init.tsx index 66d81b5aa..f89aa72c7 100644 --- a/web/lib/mobx/store-init.tsx +++ b/web/lib/mobx/store-init.tsx @@ -62,12 +62,13 @@ const MobxStoreInit = observer(() => { */ useEffect(() => { if (workspaceSlug) setWorkspaceSlug(workspaceSlug.toString()); - if (projectId) setProjectId(projectId.toString()); - if (cycleId) setCycleId(cycleId.toString()); - if (moduleId) setModuleId(moduleId.toString()); - if (globalViewId) setGlobalViewId(globalViewId.toString()); - if (viewId) setViewId(viewId.toString()); - if (inboxId) setInboxId(inboxId.toString()); + + setProjectId(projectId?.toString() ?? null); + setCycleId(cycleId?.toString() ?? null); + setModuleId(moduleId?.toString() ?? null); + setGlobalViewId(globalViewId?.toString() ?? null); + setViewId(viewId?.toString() ?? null); + setInboxId(inboxId?.toString() ?? null); }, [ workspaceSlug, projectId, diff --git a/web/store/cycle/cycles.store.ts b/web/store/cycle/cycles.store.ts index 06ac61854..15cf4a28e 100644 --- a/web/store/cycle/cycles.store.ts +++ b/web/store/cycle/cycles.store.ts @@ -32,7 +32,7 @@ export interface ICycleStore { // actions setCycleView: (_cycleView: TCycleView) => void; setCycleLayout: (_cycleLayout: TCycleLayout) => void; - setCycleId: (cycleId: string) => void; + setCycleId: (cycleId: string | null) => void; validateDate: (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => Promise; @@ -131,7 +131,7 @@ export class CycleStore implements ICycleStore { // actions setCycleView = (_cycleView: TCycleView) => (this.cycleView = _cycleView); setCycleLayout = (_cycleLayout: TCycleLayout) => (this.cycleLayout = _cycleLayout); - setCycleId = (cycleId: string) => (this.cycleId = cycleId); + setCycleId = (cycleId: string | null) => (this.cycleId = cycleId); validateDate = async (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => { try { diff --git a/web/store/global-view/global_views.store.ts b/web/store/global-view/global_views.store.ts index c9915b8d8..588f0a4d2 100644 --- a/web/store/global-view/global_views.store.ts +++ b/web/store/global-view/global_views.store.ts @@ -19,7 +19,7 @@ export interface IGlobalViewsStore { }; // actions - setGlobalViewId: (viewId: string) => void; + setGlobalViewId: (viewId: string | null) => void; fetchAllGlobalViews: (workspaceSlug: string) => Promise; fetchGlobalViewDetails: (workspaceSlug: string, viewId: string) => Promise; @@ -72,7 +72,7 @@ export class GlobalViewsStore implements IGlobalViewsStore { this.workspaceService = new WorkspaceService(); } - setGlobalViewId = (viewId: string) => { + setGlobalViewId = (viewId: string | null) => { this.globalViewId = viewId; }; diff --git a/web/store/inbox/inbox.store.ts b/web/store/inbox/inbox.store.ts index c1ca08609..b29d36855 100644 --- a/web/store/inbox/inbox.store.ts +++ b/web/store/inbox/inbox.store.ts @@ -22,7 +22,7 @@ export interface IInboxStore { }; // actions - setInboxId: (inboxId: string) => void; + setInboxId: (inboxId: string | null) => void; getInboxId: (projectId: string) => string | null; @@ -100,7 +100,7 @@ export class InboxStore implements IInboxStore { return this.inboxesList[projectId]?.[0]?.id ?? null; }; - setInboxId = (inboxId: string) => { + setInboxId = (inboxId: string | null) => { runInAction(() => { this.inboxId = inboxId; }); diff --git a/web/store/module/modules.store.ts b/web/store/module/modules.store.ts index 91a11cd76..0dc122438 100644 --- a/web/store/module/modules.store.ts +++ b/web/store/module/modules.store.ts @@ -34,7 +34,7 @@ export interface IModuleStore { }; // actions - setModuleId: (moduleSlug: string) => void; + setModuleId: (moduleId: string | null) => void; getModuleById: (moduleId: string) => IModule | null; @@ -144,8 +144,8 @@ export class ModuleStore implements IModuleStore { getModuleById = (moduleId: string) => this.moduleDetails[moduleId] || null; // actions - setModuleId = (moduleSlug: string) => { - this.moduleId = moduleSlug ?? null; + setModuleId = (moduleId: string | null) => { + this.moduleId = moduleId; }; fetchModules = async (workspaceSlug: string, projectId: string) => { diff --git a/web/store/project-view/project_views.store.ts b/web/store/project-view/project_views.store.ts index 4c4baf487..76c58002d 100644 --- a/web/store/project-view/project_views.store.ts +++ b/web/store/project-view/project_views.store.ts @@ -20,7 +20,7 @@ export interface IProjectViewsStore { }; // actions - setViewId: (viewId: string) => void; + setViewId: (viewId: string | null) => void; fetchAllViews: (workspaceSlug: string, projectId: string) => Promise; fetchViewDetails: (workspaceSlug: string, projectId: string, viewId: string) => Promise; @@ -82,7 +82,7 @@ export class ProjectViewsStore implements IProjectViewsStore { this.viewService = new ViewService(); } - setViewId = (viewId: string) => { + setViewId = (viewId: string | null) => { this.viewId = viewId; }; diff --git a/web/store/project/project.store.ts b/web/store/project/project.store.ts index 976654ee9..5b24467e7 100644 --- a/web/store/project/project.store.ts +++ b/web/store/project/project.store.ts @@ -44,7 +44,7 @@ export interface IProjectStore { currentProjectDetails: IProject | undefined; // actions - setProjectId: (projectId: string) => void; + setProjectId: (projectId: string | null) => void; setSearchQuery: (query: string) => void; getProjectById: (workspaceSlug: string, projectId: string) => IProject | null; @@ -251,8 +251,8 @@ export class ProjectStore implements IProjectStore { } // actions - setProjectId = (projectId: string) => { - this.projectId = projectId ?? null; + setProjectId = (projectId: string | null) => { + this.projectId = projectId; }; setSearchQuery = (query: string) => { From 4096136b440ca3985101b62322028c0689979acf Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:51:32 +0530 Subject: [PATCH 07/19] style: ui consistency and improvement (#2725) * style: create/update issue modal properties ui improvement * style: create update issue modal improvement * style: modal ui consistency --- .../ui/src/dropdowns/custom-search-select.tsx | 4 +- packages/ui/src/dropdowns/custom-select.tsx | 4 +- web/components/cycles/form.tsx | 2 +- web/components/issues/form.tsx | 52 ++++++++++--------- web/components/issues/select/assignee.tsx | 9 ++-- web/components/issues/select/cycle.tsx | 16 +++--- web/components/issues/select/date.tsx | 11 ++-- web/components/issues/select/estimate.tsx | 6 +-- web/components/issues/select/label.tsx | 8 +-- web/components/issues/select/module.tsx | 26 +++------- web/components/issues/select/priority.tsx | 8 ++- web/components/issues/select/project.tsx | 12 +---- web/components/issues/select/state.tsx | 8 ++- web/components/modules/select/lead.tsx | 8 ++- web/components/modules/select/members.tsx | 6 +-- .../project/create-project-modal.tsx | 6 +-- web/components/ui/date.tsx | 31 ++++++----- web/components/ui/labels-list.tsx | 2 +- web/components/views/form.tsx | 2 +- web/components/workspace/member-select.tsx | 7 +-- 20 files changed, 102 insertions(+), 126 deletions(-) diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 8ba95c28c..3f1e503a8 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -87,8 +87,8 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
-
+
diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index 2c9fb4eab..cc581ed6d 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -339,8 +339,8 @@ export const IssueForm: FC = observer((props) => { onChange={onChange} ref={ref} hasError={Boolean(errors.name)} - placeholder="Title" - className="resize-none text-xl w-full" + placeholder="Issue Title" + className="resize-none text-xl w-full focus:border-blue-400" /> )} /> @@ -348,7 +348,7 @@ export const IssueForm: FC = observer((props) => { )} {(fieldsToShow.includes("all") || fieldsToShow.includes("description")) && (
-
+
{issueName && issueName !== "" && ( } + placement="bottom-start" > {watch("parent") ? ( <> @@ -603,24 +602,27 @@ export const IssueForm: FC = observer((props) => {
-
+
setCreateMore((prevData) => !prevData)} > +
+ {}} size="sm" /> +
Create more - {}} size="md" />
-
) : ( -
- - Assignee +
+ + Assignee
)}
diff --git a/web/components/issues/select/cycle.tsx b/web/components/issues/select/cycle.tsx index 3df4948a7..742774851 100644 --- a/web/components/issues/select/cycle.tsx +++ b/web/components/issues/select/cycle.tsx @@ -55,17 +55,15 @@ export const IssueCycleSelect: React.FC = observer((props query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); const label = selectedCycle ? ( -
- - - +
+
{selectedCycle.name}
) : ( - <> +
- Select Cycle - + Cycle +
); return ( @@ -80,9 +78,7 @@ export const IssueCycleSelect: React.FC = observer((props ) : ( <> - - {label} + + {label} )}
diff --git a/web/components/issues/select/estimate.tsx b/web/components/issues/select/estimate.tsx index f2d875072..f3da8a12a 100644 --- a/web/components/issues/select/estimate.tsx +++ b/web/components/issues/select/estimate.tsx @@ -21,9 +21,9 @@ export const IssueEstimateSelect: React.FC = ({ value, onChange }) => { - - +
+ + {estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"}
diff --git a/web/components/issues/select/label.tsx b/web/components/issues/select/label.tsx index 47b1d4e5b..a85b073eb 100644 --- a/web/components/issues/select/label.tsx +++ b/web/components/issues/select/label.tsx @@ -68,10 +68,10 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, />
) : ( - - - Label - +
+ + Label +
)}
diff --git a/web/components/issues/select/module.tsx b/web/components/issues/select/module.tsx index c0d9c6c4c..cb9d6252b 100644 --- a/web/components/issues/select/module.tsx +++ b/web/components/issues/select/module.tsx @@ -55,34 +55,24 @@ export const IssueModuleSelect: React.FC = observer((pro query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); const label = selectedModule ? ( -
- - - -
{selectedModule.name}
+
+ + {selectedModule.name}
) : ( - <> +
- Select Module - + Module +
); return ( - onChange(val)} - disabled={false} - > + onChange(val)}> diff --git a/web/components/issues/select/state.tsx b/web/components/issues/select/state.tsx index 024024641..4bab99c4a 100644 --- a/web/components/issues/select/state.tsx +++ b/web/components/issues/select/state.tsx @@ -56,17 +56,15 @@ export const IssueStateSelect: React.FC = ({ setIsOpen, value, onChange, onChange={onChange} options={options} label={ -
+
{selectedOption ? ( ) : currentDefaultState ? ( ) : ( - + )} - {selectedOption?.name - ? selectedOption.name - : currentDefaultState?.name ?? State} + {selectedOption?.name ? selectedOption.name : currentDefaultState?.name ?? State}
} footerOption={ diff --git a/web/components/modules/select/lead.tsx b/web/components/modules/select/lead.tsx index 9e12e2017..69ba71bf9 100644 --- a/web/components/modules/select/lead.tsx +++ b/web/components/modules/select/lead.tsx @@ -50,9 +50,13 @@ export const ModuleLeadSelect: React.FC = ({ value, onChange }) => { {selectedOption ? ( ) : ( - + + )} + {selectedOption ? ( + selectedOption?.display_name + ) : ( + Lead )} - {selectedOption ? selectedOption?.display_name : Lead}
} onChange={onChange} diff --git a/web/components/modules/select/members.tsx b/web/components/modules/select/members.tsx index 94c74f05b..17b80a471 100644 --- a/web/components/modules/select/members.tsx +++ b/web/components/modules/select/members.tsx @@ -55,9 +55,9 @@ export const ModuleMembersSelect: React.FC = ({ value, onChange }) => { {value.length} Assignees
) : ( -
- - Assignee +
+ + Assignee
)}
diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index 6be880dcb..ff89af2c7 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -265,7 +265,7 @@ export const CreateProjectModal: FC = observer((props) => { onChange={handleNameChange(onChange)} hasError={Boolean(errors.name)} placeholder="Project Title" - className="w-full" + className="w-full focus:border-blue-400" /> )} /> @@ -298,7 +298,7 @@ export const CreateProjectModal: FC = observer((props) => { onChange={handleIdentifierChange(onChange)} hasError={Boolean(errors.identifier)} placeholder="Identifier" - className="text-xs w-full" + className="text-xs w-full focus:border-blue-400" /> )} /> @@ -316,7 +316,7 @@ export const CreateProjectModal: FC = observer((props) => { tabIndex={3} placeholder="Description..." onChange={onChange} - className="text-sm !h-24" + className="text-sm !h-24 focus:border-blue-400" hasError={Boolean(errors?.description)} /> )} diff --git a/web/components/ui/date.tsx b/web/components/ui/date.tsx index 31cbdc9fd..acc8eb1ea 100644 --- a/web/components/ui/date.tsx +++ b/web/components/ui/date.tsx @@ -21,22 +21,21 @@ export const DateSelect: React.FC = ({ value, onChange, label, minDate, m {({ close }) => ( <> - - - {value ? ( - <> - {renderShortDateWithYearFormat(value)} - - - ) : ( - <> - - {label} - - )} - + + {value ? ( + <> + + {renderShortDateWithYearFormat(value)} + + + ) : ( + <> + + {label} + + )} = (props) => { {labels && ( <> l?.name).join(", ")}> -
+
{`${labels.length} Labels`}
diff --git a/web/components/views/form.tsx b/web/components/views/form.tsx index 55ccb2fee..05169c599 100644 --- a/web/components/views/form.tsx +++ b/web/components/views/form.tsx @@ -88,7 +88,7 @@ export const ProjectViewForm: React.FC = observer(({ handleFormSubmit, ha onChange={onChange} hasError={Boolean(errors.name)} placeholder="Title" - className="resize-none text-xl" + className="resize-none w-full text-xl focus:border-blue-400" /> )} /> diff --git a/web/components/workspace/member-select.tsx b/web/components/workspace/member-select.tsx index 3ba1cbd89..bae16de01 100644 --- a/web/components/workspace/member-select.tsx +++ b/web/components/workspace/member-select.tsx @@ -48,10 +48,7 @@ export const WorkspaceMemberSelect: FC = (props) => { : options?.filter((option) => option.member.display_name.toLowerCase().includes(query.toLowerCase())); const label = ( -
+
{value ? ( <> @@ -81,7 +78,7 @@ export const WorkspaceMemberSelect: FC = (props) => {
Date: Wed, 8 Nov 2023 17:52:34 +0530 Subject: [PATCH 08/19] fix: bug fixes and ui improvements (#2703) * fix: gantt chart duration in decimal * fix: Loading text instead Spinner in peek view * fix: cycle more popover typo & icon overlapping * fix: list layout properties alignment * fix: project search empty state * fix: calendar layout issue text overflow & redirection inconsistency * style: urgent priority hover background color * fix: Cycle issues kanban layout empty state missing * style: custom snooze modal placeholder text color * refactor: replaced unwanted anchor tag with div * chore: removed empty state for cycle kanban layout --- web/components/cycles/cycles-list-item.tsx | 4 +- .../issue-layouts/calendar/issue-blocks.tsx | 63 +++++++++---------- .../issues/issue-layouts/list/properties.tsx | 2 +- .../issues/issue-peek-overview/view.tsx | 6 +- .../select-snooze-till-modal.tsx | 2 +- web/components/project/card-list.tsx | 16 +++-- web/components/project/priority-select.tsx | 10 ++- web/helpers/date-time.helper.ts | 2 +- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 097a18070..d104ae5e5 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -231,7 +231,7 @@ export const CyclesListItem: FC = (props) => { )} - + {!isCompleted && ( <> @@ -243,7 +243,7 @@ export const CyclesListItem: FC = (props) => { - Delete module + Delete cycle diff --git a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx index c4795739c..2e270ab7d 100644 --- a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -34,39 +34,38 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { {issue?.tempId !== undefined && (
)} diff --git a/web/components/issues/issue-layouts/list/properties.tsx b/web/components/issues/issue-layouts/list/properties.tsx index 92a203f36..58944c76c 100644 --- a/web/components/issues/issue-layouts/list/properties.tsx +++ b/web/components/issues/issue-layouts/list/properties.tsx @@ -54,7 +54,7 @@ export const KanBanProperties: FC = observer((props) => { }; return ( -
+
{/* basic properties */} {/* state */} {displayProperties && displayProperties?.state && ( diff --git a/web/components/issues/issue-peek-overview/view.tsx b/web/components/issues/issue-peek-overview/view.tsx index 78557d865..9268c2d69 100644 --- a/web/components/issues/issue-peek-overview/view.tsx +++ b/web/components/issues/issue-peek-overview/view.tsx @@ -7,7 +7,7 @@ import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react"; import { PeekOverviewIssueDetails } from "./issue-detail"; import { PeekOverviewProperties } from "./properties"; import { IssueComment } from "./activity"; -import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon } from "@plane/ui"; +import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui"; import { DeleteIssueModal } from "../delete-issue-modal"; import { DeleteArchivedIssueModal } from "../delete-archived-issue-modal"; // types @@ -154,7 +154,7 @@ export const IssueView: FC = observer((props) => { onSubmit={handleDeleteIssue} /> )} -
+
{children && (
{children} @@ -251,7 +251,7 @@ export const IssueView: FC = observer((props) => {
)} {isLoading && !issue ? ( -
Loading...
+
) : ( issue && ( <> diff --git a/web/components/notifications/select-snooze-till-modal.tsx b/web/components/notifications/select-snooze-till-modal.tsx index 4cc13a50d..e434bcae8 100644 --- a/web/components/notifications/select-snooze-till-modal.tsx +++ b/web/components/notifications/select-snooze-till-modal.tsx @@ -171,7 +171,7 @@ export const SnoozeNotificationModal: FC = (props) => { setValue("time", null); onChange(val); }} - className="px-3 py-2 w-full rounded-md border border-custom-border-300 bg-custom-background-100 text-custom-text-100 focus:outline-none !text-sm" + className="px-3 py-2 w-full rounded-md border border-custom-border-300 bg-custom-background-100 text-custom-text-100 placeholder:!text-custom-text-400 focus:outline-none !text-sm" wrapperClassName="w-full" noBorder minDate={new Date()} diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 0a090f636..f364be084 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -38,12 +38,16 @@ export const ProjectCardList: FC = observer((props) => { return ( <> {projects.length > 0 ? ( -
-
- {projectStore.searchedProjects.map((project) => ( - - ))} -
+
+ {projectStore.searchedProjects.length == 0 ? ( +
No matching projects
+ ) : ( +
+ {projectStore.searchedProjects.map((project) => ( + + ))} +
+ )}
) : ( = ({ ? "border-red-500/20 bg-red-500" : "border-custom-border-300" : "border-custom-border-300" - } ${!disabled ? "hover:bg-custom-background-80" : ""} ${ - disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer" - } ${buttonClassName}`} + } ${ + !disabled + ? `${ + value === "urgent" && highlightUrgentPriority ? "hover:bg-red-400" : "hover:bg-custom-background-80" + }` + : "" + } ${disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer"} ${buttonClassName}`} > {label} {!hideDropdownArrow && !disabled &&
); From 2d713777228d91c898f3b29b7493e47623f4e29b Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:54:59 +0530 Subject: [PATCH 10/19] fix: added empty project state when no project exists. (#2727) * fix: added empty project state when no project exists * fix: duplicate import --- .../empty-states/global-view.tsx | 46 ++++++++++++++----- .../roots/global-view-layout-root.tsx | 5 +- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/web/components/issues/issue-layouts/empty-states/global-view.tsx b/web/components/issues/issue-layouts/empty-states/global-view.tsx index 7151b5ddc..bbe5f16a5 100644 --- a/web/components/issues/issue-layouts/empty-states/global-view.tsx +++ b/web/components/issues/issue-layouts/empty-states/global-view.tsx @@ -1,27 +1,49 @@ +// next +import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -import { PlusIcon } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components import { EmptyState } from "components/common"; // assets import emptyIssue from "public/empty-state/issue.svg"; +import emptyProject from "public/empty-state/project.svg"; +// icons +import { Plus, PlusIcon } from "lucide-react"; export const GlobalViewEmptyState: React.FC = observer(() => { - const { commandPalette: commandPaletteStore } = useMobxStore(); + const router = useRouter(); + const { workspaceSlug } = router.query; + + const { commandPalette: commandPaletteStore, project: projectStore } = useMobxStore(); + + const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; return (
- , - onClick: () => commandPaletteStore.toggleCreateIssueModal(true), - }} - /> + {!projects || projects?.length === 0 ? ( + , + text: "New Project", + onClick: () => commandPaletteStore.toggleCreateProjectModal(true), + }} + /> + ) : ( + , + onClick: () => commandPaletteStore.toggleCreateIssueModal(true), + }} + /> + )}
); }); 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 018a63d2a..345a33db3 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 @@ -28,12 +28,15 @@ export const GlobalViewLayoutRoot: React.FC = observer((props) => { workspaceFilter: workspaceFilterStore, workspace: workspaceStore, issueDetail: issueDetailStore, + project: projectStore, } = useMobxStore(); const viewDetails = globalViewId ? globalViewsStore.globalViewDetails[globalViewId.toString()] : undefined; const storedFilters = globalViewId ? globalViewFiltersStore.storedFilters[globalViewId.toString()] : undefined; + const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; + useSWR( workspaceSlug && globalViewId && viewDetails ? `GLOBAL_VIEW_ISSUES_${globalViewId.toString()}` : null, workspaceSlug && globalViewId && viewDetails @@ -94,7 +97,7 @@ export const GlobalViewLayoutRoot: React.FC = observer((props) => { return (
- {issues?.length === 0 ? ( + {issues?.length === 0 || !projects || projects?.length === 0 ? ( ) : (
From f8002852e0857714ab2c610e7ed459b012aadbe8 Mon Sep 17 00:00:00 2001 From: sabith-tu <95301637+sabith-tu@users.noreply.github.com> Date: Wed, 8 Nov 2023 17:55:28 +0530 Subject: [PATCH 11/19] fix: issue property height and peek view date picker border radius (#2726) --- web/components/issues/issue-layouts/properties/estimates.tsx | 2 +- web/components/issues/issue-layouts/properties/labels.tsx | 2 +- web/components/issues/issue-layouts/properties/state.tsx | 2 +- web/components/issues/issue-peek-overview/properties.tsx | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/components/issues/issue-layouts/properties/estimates.tsx b/web/components/issues/issue-layouts/properties/estimates.tsx index 77b5a79b3..432a39f4d 100644 --- a/web/components/issues/issue-layouts/properties/estimates.tsx +++ b/web/components/issues/issue-layouts/properties/estimates.tsx @@ -110,7 +110,7 @@ export const IssuePropertyEstimates: React.FC = observe
- } - position={position} - renderTarget={({ isOpen: isTooltipOpen, ref: eleReference, ...tooltipProps }) => - React.cloneElement(children, { ref: eleReference, ...tooltipProps, ...children.props }) - } - /> - ); -}; diff --git a/packages/editor/core/src/ui/plugins/delete-image.tsx b/packages/editor/core/src/ui/plugins/delete-image.tsx index 56284472b..48ec244fc 100644 --- a/packages/editor/core/src/ui/plugins/delete-image.tsx +++ b/packages/editor/core/src/ui/plugins/delete-image.tsx @@ -15,7 +15,11 @@ interface ImageNode extends ProseMirrorNode { const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => new Plugin({ key: deleteKey, - appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { + appendTransaction: ( + transactions: readonly Transaction[], + oldState: EditorState, + newState: EditorState, + ) => { const newImageSources = new Set(); newState.doc.descendants((node) => { if (node.type.name === IMAGE_NODE_TYPE) { @@ -55,7 +59,10 @@ const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => export default TrackImageDeletionPlugin; -async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise { +async function onNodeDeleted( + src: string, + deleteImage: DeleteImage, +): Promise { try { const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); const resStatus = await deleteImage(assetUrlWithWorkspaceId); diff --git a/packages/editor/core/src/ui/plugins/upload-image.tsx b/packages/editor/core/src/ui/plugins/upload-image.tsx index cdd62ae48..256460073 100644 --- a/packages/editor/core/src/ui/plugins/upload-image.tsx +++ b/packages/editor/core/src/ui/plugins/upload-image.tsx @@ -4,7 +4,7 @@ import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view"; const uploadKey = new PluginKey("upload-image"); -const UploadImagesPlugin = () => +const UploadImagesPlugin = (cancelUploadImage?: () => any) => new Plugin({ key: uploadKey, state: { @@ -21,15 +21,46 @@ const UploadImagesPlugin = () => const placeholder = document.createElement("div"); placeholder.setAttribute("class", "img-placeholder"); const image = document.createElement("img"); - image.setAttribute("class", "opacity-10 rounded-lg border border-custom-border-300"); + image.setAttribute( + "class", + "opacity-10 rounded-lg border border-custom-border-300", + ); image.src = src; placeholder.appendChild(image); + + // Create cancel button + const cancelButton = document.createElement("button"); + cancelButton.style.position = "absolute"; + cancelButton.style.right = "3px"; + cancelButton.style.top = "3px"; + cancelButton.setAttribute("class", "opacity-90 rounded-lg"); + + cancelButton.onclick = () => { + cancelUploadImage?.(); + }; + + // Create an SVG element from the SVG string + const svgString = ``; + const parser = new DOMParser(); + const svgElement = parser.parseFromString( + svgString, + "image/svg+xml", + ).documentElement; + + cancelButton.appendChild(svgElement); + placeholder.appendChild(cancelButton); const deco = Decoration.widget(pos + 1, placeholder, { id, }); set = set.add(tr.doc, [deco]); } else if (action && action.remove) { - set = set.remove(set.find(undefined, undefined, (spec) => spec.id == action.remove.id)); + set = set.remove( + set.find( + undefined, + undefined, + (spec) => spec.id == action.remove.id, + ), + ); } return set; }, @@ -48,19 +79,39 @@ function findPlaceholder(state: EditorState, id: {}) { const found = decos.find( undefined, undefined, - (spec: { id: number | undefined }) => spec.id == id + (spec: { id: number | undefined }) => spec.id == id, ); return found.length ? found[0].from : null; } +const removePlaceholder = (view: EditorView, id: {}) => { + const removePlaceholderTr = view.state.tr.setMeta(uploadKey, { + remove: { id }, + }); + view.dispatch(removePlaceholderTr); +}; + export async function startImageUpload( file: File, view: EditorView, pos: number, uploadFile: UploadImage, - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void + setIsSubmitting?: ( + isSubmitting: "submitting" | "submitted" | "saved", + ) => void, ) { + if (!file) { + alert("No file selected. Please select a file to upload."); + return; + } + if (!file.type.includes("image/")) { + alert("Invalid file type. Please select an image file."); + return; + } + + if (file.size > 5 * 1024 * 1024) { + alert("File size too large. Please select a file smaller than 5MB."); return; } @@ -82,28 +133,42 @@ export async function startImageUpload( view.dispatch(tr); }; + // Handle FileReader errors + reader.onerror = (error) => { + console.error("FileReader error: ", error); + removePlaceholder(view, id); + return; + }; + setIsSubmitting?.("submitting"); - const src = await UploadImageHandler(file, uploadFile); - const { schema } = view.state; - pos = findPlaceholder(view.state, id); - if (pos == null) return; - const imageSrc = typeof src === "object" ? reader.result : src; + try { + const src = await UploadImageHandler(file, uploadFile); + const { schema } = view.state; + pos = findPlaceholder(view.state, id); - const node = schema.nodes.image.create({ src: imageSrc }); - const transaction = view.state.tr - .replaceWith(pos, pos, node) - .setMeta(uploadKey, { remove: { id } }); - view.dispatch(transaction); + if (pos == null) return; + const imageSrc = typeof src === "object" ? reader.result : src; + + const node = schema.nodes.image.create({ src: imageSrc }); + const transaction = view.state.tr + .replaceWith(pos, pos, node) + .setMeta(uploadKey, { remove: { id } }); + view.dispatch(transaction); + } catch (error) { + console.error("Upload error: ", error); + removePlaceholder(view, id); + } } -const UploadImageHandler = (file: File, - uploadFile: UploadImage +const UploadImageHandler = ( + file: File, + uploadFile: UploadImage, ): Promise => { try { return new Promise(async (resolve, reject) => { try { - const imageUrl = await uploadFile(file) + const imageUrl = await uploadFile(file); const image = new Image(); image.src = imageUrl; @@ -118,9 +183,6 @@ const UploadImageHandler = (file: File, } }); } catch (error) { - if (error instanceof Error) { - console.log(error.message); - } return Promise.reject(error); } }; diff --git a/packages/editor/lite-text-editor/package.json b/packages/editor/lite-text-editor/package.json index 3b6cd720b..52f27fb29 100644 --- a/packages/editor/lite-text-editor/package.json +++ b/packages/editor/lite-text-editor/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@plane/editor-core": "*", + "@plane/ui": "*", "@tiptap/extension-list-item": "^2.1.11", "class-variance-authority": "^0.7.0", "clsx": "^1.2.1", diff --git a/packages/editor/lite-text-editor/src/ui/index.tsx b/packages/editor/lite-text-editor/src/ui/index.tsx index ef321d511..26df08025 100644 --- a/packages/editor/lite-text-editor/src/ui/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/index.tsx @@ -47,6 +47,7 @@ interface ILiteTextEditor { }[]; }; onEnterKeyPress?: (e?: any) => void; + cancelUploadImage?: () => any; mentionHighlights?: string[]; mentionSuggestions?: IMentionSuggestion[]; submitButton?: React.ReactNode; @@ -64,6 +65,7 @@ interface EditorHandle { const LiteTextEditor = (props: LiteTextEditorProps) => { const { onChange, + cancelUploadImage, debouncedUpdatesEnabled, setIsSubmitting, setShouldShowAlert, @@ -84,6 +86,7 @@ const LiteTextEditor = (props: LiteTextEditorProps) => { const editor = useEditor({ onChange, + cancelUploadImage, debouncedUpdatesEnabled, setIsSubmitting, setShouldShowAlert, diff --git a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx index cf0d78688..a4fb0479c 100644 --- a/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx +++ b/packages/editor/lite-text-editor/src/ui/menus/fixed-menu/index.tsx @@ -14,8 +14,8 @@ import { TableItem, UnderLineItem, } from "@plane/editor-core"; -import { Tooltip } from "../../tooltip"; -import { UploadImage } from "../.."; +import { Tooltip } from "@plane/ui"; +import { UploadImage } from "../../"; export interface BubbleMenuItem { name: string; diff --git a/packages/editor/rich-text-editor/src/ui/index.tsx b/packages/editor/rich-text-editor/src/ui/index.tsx index a0dbe7226..2e98a72aa 100644 --- a/packages/editor/rich-text-editor/src/ui/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/index.tsx @@ -1,8 +1,13 @@ -"use client" -import * as React from 'react'; -import { EditorContainer, EditorContentWrapper, getEditorClassNames, useEditor } from '@plane/editor-core'; -import { EditorBubbleMenu } from './menus/bubble-menu'; -import { RichTextEditorExtensions } from './extensions'; +"use client"; +import * as React from "react"; +import { + EditorContainer, + EditorContentWrapper, + getEditorClassNames, + useEditor, +} from "@plane/editor-core"; +import { EditorBubbleMenu } from "./menus/bubble-menu"; +import { RichTextEditorExtensions } from "./extensions"; export type UploadImage = (file: File) => Promise; export type DeleteImage = (assetUrlWithWorkspaceId: string) => Promise; @@ -14,9 +19,9 @@ export type IMentionSuggestion = { title: string; subtitle: string; redirect_uri: string; -} +}; -export type IMentionHighlight = string +export type IMentionHighlight = string; interface IRichTextEditor { value: string; @@ -24,10 +29,13 @@ interface IRichTextEditor { deleteFile: DeleteImage; noBorder?: boolean; borderOnFocus?: boolean; + cancelUploadImage?: () => any; customClassName?: string; editorContentCustomClassNames?: string; onChange?: (json: any, html: string) => void; - setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void; + setIsSubmitting?: ( + isSubmitting: "submitting" | "submitted" | "saved", + ) => void; setShouldShowAlert?: (showAlert: boolean) => void; forwardedRef?: any; debouncedUpdatesEnabled?: boolean; @@ -54,11 +62,12 @@ const RichTextEditor = ({ uploadFile, deleteFile, noBorder, + cancelUploadImage, borderOnFocus, customClassName, forwardedRef, mentionHighlights, - mentionSuggestions + mentionSuggestions, }: RichTextEditorProps) => { const editor = useEditor({ onChange, @@ -67,14 +76,19 @@ const RichTextEditor = ({ setShouldShowAlert, value, uploadFile, + cancelUploadImage, deleteFile, forwardedRef, extensions: RichTextEditorExtensions(uploadFile, setIsSubmitting), mentionHighlights, - mentionSuggestions + mentionSuggestions, }); - const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName }); + const editorClassNames = getEditorClassNames({ + noBorder, + borderOnFocus, + customClassName, + }); if (!editor) return null; @@ -82,16 +96,19 @@ const RichTextEditor = ({ {editor && }
- +
-
+ ); }; -const RichTextEditorWithRef = React.forwardRef((props, ref) => ( - -)); +const RichTextEditorWithRef = React.forwardRef( + (props, ref) => , +); RichTextEditorWithRef.displayName = "RichTextEditorWithRef"; -export { RichTextEditor, RichTextEditorWithRef}; +export { RichTextEditor, RichTextEditorWithRef }; diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index f70a2c5aa..9878fd00a 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -76,6 +76,7 @@ export const AddComment: React.FC = observer((props) => { handleSubmit(onSubmit)(e); }); }} + cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspace_slug as string)} deleteFile={fileService.deleteImage} ref={editorRef} diff --git a/space/components/issues/peek-overview/comment/comment-detail-card.tsx b/space/components/issues/peek-overview/comment/comment-detail-card.tsx index 29801c9e6..ab09b2490 100644 --- a/space/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/space/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -103,6 +103,7 @@ export const CommentCard: React.FC = observer((props) => { render={({ field: { onChange, value } }) => ( { + this.cancelSource = axios.CancelToken.source(); return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, { headers: { ...this.getHeaders(), "Content-Type": "multipart/form-data", }, + cancelToken: this.cancelSource.token, }) .then((response) => response?.data) .catch((error) => { - throw error?.response?.data; + if (axios.isCancel(error)) { + console.log(error.message); + } else { + throw error?.response?.data; + } }); } + cancelUpload() { + this.cancelSource.cancel("Upload cancelled"); + } getUploadFileFunction(workspaceSlug: string): (file: File) => Promise { return async (file: File) => { const formData = new FormData(); diff --git a/space/styles/table.css b/space/styles/table.css index ad88fd10e..8a47a8c59 100644 --- a/space/styles/table.css +++ b/space/styles/table.css @@ -92,7 +92,7 @@ transform: translateY(-50%); } -.tableWrapper .tableControls .columnsControl > button { +.tableWrapper .tableControls .columnsControl .columnsControlDiv { color: white; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M4.5 10.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S6 12.825 6 12s-.675-1.5-1.5-1.5zm15 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S21 12.825 21 12s-.675-1.5-1.5-1.5zm-7.5 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); width: 30px; @@ -104,26 +104,42 @@ transform: translateX(-50%); } -.tableWrapper .tableControls .rowsControl > button { +.tableWrapper .tableControls .rowsControl .rowsControlDiv { color: white; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M12 3c-.825 0-1.5.675-1.5 1.5S11.175 6 12 6s1.5-.675 1.5-1.5S12.825 3 12 3zm0 15c-.825 0-1.5.675-1.5 1.5S11.175 21 12 21s1.5-.675 1.5-1.5S12.825 18 12 18zm0-7.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); height: 30px; width: 15px; } -.tableWrapper .tableControls button { +.tableWrapper .tableControls .rowsControlDiv { background-color: rgba(var(--color-primary-100)); border: 1px solid rgba(var(--color-border-200)); border-radius: 2px; background-size: 1.25rem; background-repeat: no-repeat; background-position: center; - transition: transform ease-out 100ms, background-color ease-out 100ms; + transition: + transform ease-out 100ms, + background-color ease-out 100ms; outline: none; box-shadow: #000 0px 2px 4px; cursor: pointer; } +.tableWrapper .tableControls .columnsControlDiv { + background-color: rgba(var(--color-primary-100)); + border: 1px solid rgba(var(--color-border-200)); + border-radius: 2px; + background-size: 1.25rem; + background-repeat: no-repeat; + background-position: center; + transition: + transform ease-out 100ms, + background-color ease-out 100ms; + outline: none; + box-shadow: #000 0px 2px 4px; + cursor: pointer; +} .tableWrapper .tableControls .tableToolbox, .tableWrapper .tableControls .tableColorPickerToolbox { border: 1px solid rgba(var(--color-border-300)); diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx index a468580f4..9a1d15099 100644 --- a/web/components/inbox/modals/create-issue-modal.tsx +++ b/web/components/inbox/modals/create-issue-modal.tsx @@ -15,6 +15,7 @@ import { IssuePrioritySelect } from "components/issues/select"; import { Button, Input, ToggleSwitch } from "@plane/ui"; // types import { IIssue } from "types"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; type Props = { isOpen: boolean; @@ -40,6 +41,8 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { const editorRef = useRef(null); + const editorSuggestion = useEditorSuggestions() + const router = useRouter(); const { workspaceSlug, projectId, inboxId } = router.query; @@ -134,6 +137,7 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { control={control} render={({ field: { value, onChange } }) => ( = observer((props) => { onChange={(description, description_html: string) => { onChange(description_html); }} + mentionSuggestions={editorSuggestion.mentionSuggestions} + mentionHighlights={editorSuggestion.mentionHighlights} /> )} /> diff --git a/web/components/issues/comment/add-comment.tsx b/web/components/issues/comment/add-comment.tsx index 28c986bc7..ee7805ef7 100644 --- a/web/components/issues/comment/add-comment.tsx +++ b/web/components/issues/comment/add-comment.tsx @@ -84,6 +84,7 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc render={({ field: { onChange: onCommentChange, value: commentValue } }) => ( = ({
= (props) => { control={control} render={({ field: { value, onChange } }) => ( = (props) => { control={control} render={({ field: { value, onChange } }) => ( = observer((props) => { control={control} render={({ field: { value, onChange } }) => ( = (props) => {
= (props) => { render={({ field: { onChange: onCommentChange, value: commentValue } }) => ( = (props) =
{errors.name ? errors.name.message : null} = ({ if (!data) return ( = ({ return ( = ({ block, projectDetails, showBl {showBlockDetails ? block.description_html.length > 7 && ( { + this.cancelSource = axios.CancelToken.source(); return this.post(`/api/workspaces/${workspaceSlug}/file-assets/`, file, { headers: { ...this.getHeaders(), "Content-Type": "multipart/form-data", }, + cancelToken: this.cancelSource.token, }) .then((response) => response?.data) .catch((error) => { - throw error?.response?.data; + if (axios.isCancel(error)) { + console.log(error.message); + } else { + throw error?.response?.data; + } }); } + cancelUpload() { + this.cancelSource.cancel("Upload cancelled"); + } + getUploadFileFunction(workspaceSlug: string): (file: File) => Promise { return async (file: File) => { const formData = new FormData(); diff --git a/web/styles/table.css b/web/styles/table.css index ad88fd10e..bce7e4683 100644 --- a/web/styles/table.css +++ b/web/styles/table.css @@ -92,7 +92,7 @@ transform: translateY(-50%); } -.tableWrapper .tableControls .columnsControl > button { +.tableWrapper .tableControls .columnsControl .columnsControlDiv { color: white; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M4.5 10.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S6 12.825 6 12s-.675-1.5-1.5-1.5zm15 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S21 12.825 21 12s-.675-1.5-1.5-1.5zm-7.5 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); width: 30px; @@ -104,14 +104,14 @@ transform: translateX(-50%); } -.tableWrapper .tableControls .rowsControl > button { +.tableWrapper .tableControls .rowsControl .rowsControlDiv { color: white; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='24' height='24'%3E%3Cpath fill='none' d='M0 0h24v24H0z'/%3E%3Cpath fill='%238F95B2' d='M12 3c-.825 0-1.5.675-1.5 1.5S11.175 6 12 6s1.5-.675 1.5-1.5S12.825 3 12 3zm0 15c-.825 0-1.5.675-1.5 1.5S11.175 21 12 21s1.5-.675 1.5-1.5S12.825 18 12 18zm0-7.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z'/%3E%3C/svg%3E"); height: 30px; width: 15px; } -.tableWrapper .tableControls button { +.tableWrapper .tableControls .rowsControlDiv { background-color: rgba(var(--color-primary-100)); border: 1px solid rgba(var(--color-border-200)); border-radius: 2px; @@ -124,6 +124,18 @@ cursor: pointer; } +.tableWrapper .tableControls .columnsControlDiv { + background-color: rgba(var(--color-primary-100)); + border: 1px solid rgba(var(--color-border-200)); + border-radius: 2px; + background-size: 1.25rem; + background-repeat: no-repeat; + background-position: center; + transition: transform ease-out 100ms, background-color ease-out 100ms; + outline: none; + box-shadow: #000 0px 2px 4px; + cursor: pointer; +} .tableWrapper .tableControls .tableToolbox, .tableWrapper .tableControls .tableColorPickerToolbox { border: 1px solid rgba(var(--color-border-300)); From bd1a850f3557603aae87b93d393ddf1862a8c377 Mon Sep 17 00:00:00 2001 From: Ramesh Kumar Chandra <31303617+rameshkumarchandra@users.noreply.github.com> Date: Wed, 8 Nov 2023 18:12:36 +0530 Subject: [PATCH 14/19] style: kanban card label overflow (#2722) * chore: kanban card lable drop down items overflow * style: kaban card label text overflow, tool tip, hover cursor * style: label overflow in list layout --- .../issue-layouts/properties/labels.tsx | 66 +++++++++++-------- .../issues/issue-layouts/properties/state.tsx | 4 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/web/components/issues/issue-layouts/properties/labels.tsx b/web/components/issues/issue-layouts/properties/labels.tsx index 3f0f6552d..1b35f0495 100644 --- a/web/components/issues/issue-layouts/properties/labels.tsx +++ b/web/components/issues/issue-layouts/properties/labels.tsx @@ -65,14 +65,14 @@ export const IssuePropertyLabels: React.FC = observer((pro value: label.id, query: label.name, content: ( -
+
- {label.name} +
{label.name}
), })); @@ -93,31 +93,39 @@ export const IssuePropertyLabels: React.FC = observer((pro }); const label = ( -
+
{value.length > 0 ? ( value.length <= maxRender ? ( <> {(projectLabels ? projectLabels : []) ?.filter((l) => value.includes(l.id)) .map((label) => ( -
-
- - {label.name} +
+
+ +
+ {label.name} +
+
-
+ ))} ) : ( -
+
= observer((pro ) ) : (
Select labels
@@ -148,7 +155,7 @@ export const IssuePropertyLabels: React.FC = observer((pro return ( = observer((pro ); - } + }, ); Button.displayName = "plane-ui-button"; diff --git a/packages/ui/src/button/helper.tsx b/packages/ui/src/button/helper.tsx index 82489c3e8..48b1fc94a 100644 --- a/packages/ui/src/button/helper.tsx +++ b/packages/ui/src/button/helper.tsx @@ -102,7 +102,7 @@ export const buttonStyling: IButtonStyling = { export const getButtonStyling = ( variant: TButtonVariant, size: TButtonSizes, - disabled: boolean = false + disabled: boolean = false, ): string => { let _variant: string = ``; const currentVariant = buttonStyling[variant]; diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 3f1e503a8..0fb4c67cf 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -35,7 +35,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState( - null + null, ); const { styles, attributes } = usePopper(referenceElement, popperElement, { @@ -46,7 +46,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { query === "" ? options : options?.filter((option) => - option.query.toLowerCase().includes(query.toLowerCase()) + option.query.toLowerCase().includes(query.toLowerCase()), ); const comboboxProps: any = { diff --git a/packages/ui/src/dropdowns/custom-select.tsx b/packages/ui/src/dropdowns/custom-select.tsx index dd4d1d786..b62ff2cb3 100644 --- a/packages/ui/src/dropdowns/custom-select.tsx +++ b/packages/ui/src/dropdowns/custom-select.tsx @@ -30,7 +30,7 @@ const CustomSelect = (props: ICustomSelectProps) => { const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState( - null + null, ); const { styles, attributes } = usePopper(referenceElement, popperElement, { diff --git a/packages/ui/src/form-fields/index.ts b/packages/ui/src/form-fields/index.ts index 49f6f1552..9cac73428 100644 --- a/packages/ui/src/form-fields/index.ts +++ b/packages/ui/src/form-fields/index.ts @@ -1,3 +1,3 @@ export * from "./input"; export * from "./textarea"; -export * from "./input-color-picker" \ No newline at end of file +export * from "./input-color-picker"; diff --git a/packages/ui/src/form-fields/textarea.tsx b/packages/ui/src/form-fields/textarea.tsx index e53979edc..8490326b8 100644 --- a/packages/ui/src/form-fields/textarea.tsx +++ b/packages/ui/src/form-fields/textarea.tsx @@ -10,7 +10,7 @@ export interface TextAreaProps // Updates the height of a