From dadd2cf39b77e1d4299e935b34c168d9e56c27c5 Mon Sep 17 00:00:00 2001 From: Alexander Pushkov Date: Wed, 13 Dec 2023 19:04:12 +0300 Subject: [PATCH 01/24] Fix .vertical-lr in Firefox --- web/styles/globals.css | 1 + 1 file changed, 1 insertion(+) diff --git a/web/styles/globals.css b/web/styles/globals.css index e94a751f3..8f77c8669 100644 --- a/web/styles/globals.css +++ b/web/styles/globals.css @@ -420,6 +420,7 @@ body { .vertical-lr { -webkit-writing-mode: vertical-lr; -ms-writing-mode: vertical-lr; + writing-mode: vertical-lr; } div.web-view-spinner { From 08425c9614874d813de60db6f68c14516cb9b1c5 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 15 Dec 2023 17:21:06 +0530 Subject: [PATCH 02/24] fix: sync changes (#3151) --- .github/workflows/create-sync-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-sync-pr.yml b/.github/workflows/create-sync-pr.yml index 0f85e940c..0203ab0d8 100644 --- a/.github/workflows/create-sync-pr.yml +++ b/.github/workflows/create-sync-pr.yml @@ -51,5 +51,5 @@ jobs: gh pr create \ --base $TARGET_BASE_BRANCH \ --head $TARGET_BRANCH \ - --title "$PR_TITLE" \ + --title "${PR_TITLE}" \ --repo $TARGET_REPO From 74b141eea21b03d2a89ba8705ccd866222d5dcb8 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 15 Dec 2023 17:24:24 +0530 Subject: [PATCH 03/24] fix: sync changes (#3152) --- .github/workflows/create-sync-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-sync-pr.yml b/.github/workflows/create-sync-pr.yml index 0203ab0d8..540d8fe0f 100644 --- a/.github/workflows/create-sync-pr.yml +++ b/.github/workflows/create-sync-pr.yml @@ -41,15 +41,15 @@ jobs: TARGET_BRANCH="${{ secrets.SYNC_TARGET_BRANCH_NAME }}" TARGET_BASE_BRANCH="${{ secrets.SYNC_TARGET_BASE_BRANCH_NAME }}" SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}" + PR_TITLE="${{secrets.SYNC_PR_TITLE}}" git checkout $SOURCE_BRANCH git remote add target-origin "https://$GH_TOKEN@github.com/$TARGET_REPO.git" git push target-origin $SOURCE_BRANCH:$TARGET_BRANCH - PR_TITLE=${{secrets.SYNC_PR_TITLE}} gh pr create \ --base $TARGET_BASE_BRANCH \ --head $TARGET_BRANCH \ - --title "${PR_TITLE}" \ + --title $PR_TITLE \ --repo $TARGET_REPO From ce9714ff126a0b6e50377e1f02fb38444b8a0c5e Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 15 Dec 2023 17:31:14 +0530 Subject: [PATCH 04/24] fix: sync pr changes (#3153) --- .github/workflows/create-sync-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-sync-pr.yml b/.github/workflows/create-sync-pr.yml index 540d8fe0f..ba9cb47be 100644 --- a/.github/workflows/create-sync-pr.yml +++ b/.github/workflows/create-sync-pr.yml @@ -51,5 +51,5 @@ jobs: gh pr create \ --base $TARGET_BASE_BRANCH \ --head $TARGET_BRANCH \ - --title $PR_TITLE \ + --fill-first \ --repo $TARGET_REPO From 885de6f679ea54ea655302c26799b3f92089213f Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 15 Dec 2023 17:36:26 +0530 Subject: [PATCH 05/24] fix: sync changes (#3154) --- .github/workflows/create-sync-pr.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/create-sync-pr.yml b/.github/workflows/create-sync-pr.yml index ba9cb47be..0bf08c8cf 100644 --- a/.github/workflows/create-sync-pr.yml +++ b/.github/workflows/create-sync-pr.yml @@ -51,5 +51,6 @@ jobs: gh pr create \ --base $TARGET_BASE_BRANCH \ --head $TARGET_BRANCH \ - --fill-first \ + --title "sync changes" \ + --body "sync changes" \ --repo $TARGET_REPO From b7e2f1e57abb52a9d084980d59e0fa4e960d9bad Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Fri, 15 Dec 2023 17:37:37 +0530 Subject: [PATCH 06/24] chore: resolve priority sorting order (#3141) --- web/constants/issue.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/web/constants/issue.ts b/web/constants/issue.ts index 965ab69cc..945a73b38 100644 --- a/web/constants/issue.ts +++ b/web/constants/issue.ts @@ -78,7 +78,7 @@ export const ISSUE_ORDER_BY_OPTIONS: { { key: "-updated_at", title: "Last Updated" }, { key: "start_date", title: "Start Date" }, { key: "target_date", title: "Due Date" }, - { key: "priority", title: "Priority" }, + { key: "-priority", title: "Priority" }, ]; export const ISSUE_FILTER_OPTIONS: { @@ -227,7 +227,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels", null], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -240,7 +240,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels"], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -255,7 +255,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state", "state_detail.group", "priority", "labels", "assignees", "created_by", null], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -270,7 +270,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels", null], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -283,7 +283,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels"], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -311,7 +311,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_properties: true, display_filters: { group_by: ["state", "priority", "labels", "assignees", "created_by", null], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -325,7 +325,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { display_filters: { group_by: ["state", "priority", "labels", "assignees", "created_by"], sub_group_by: ["state", "priority", "labels", "assignees", "created_by", null], - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority", "target_date"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority", "target_date"], type: [null, "active", "backlog"], }, extra_options: { @@ -348,7 +348,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { filters: ["priority", "state", "assignees", "mentions", "created_by", "labels", "start_date", "target_date"], display_properties: true, display_filters: { - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { @@ -360,7 +360,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { filters: ["priority", "state", "assignees", "mentions", "created_by", "labels", "start_date", "target_date"], display_properties: false, display_filters: { - order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], + order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"], type: [null, "active", "backlog"], }, extra_options: { From f03a9a6de8769ed1daa5851a1f8407c269334217 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Fri, 15 Dec 2023 18:12:16 +0530 Subject: [PATCH 07/24] fix: sync changes (#3156) * fix: sync changes * fix: sync changes * fix: sync changes updated --- .github/workflows/create-sync-pr.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/create-sync-pr.yml b/.github/workflows/create-sync-pr.yml index 0bf08c8cf..5b5f958d3 100644 --- a/.github/workflows/create-sync-pr.yml +++ b/.github/workflows/create-sync-pr.yml @@ -3,7 +3,7 @@ name: Create Sync Action on: pull_request: branches: - - develop # Change this to preview + - preview types: - closed env: @@ -33,24 +33,14 @@ jobs: sudo apt update sudo apt install gh -y - - name: Create Pull Request + - name: Push Changes to Target Repo env: GH_TOKEN: ${{ secrets.ACCESS_TOKEN }} run: | TARGET_REPO="${{ secrets.SYNC_TARGET_REPO_NAME }}" TARGET_BRANCH="${{ secrets.SYNC_TARGET_BRANCH_NAME }}" - TARGET_BASE_BRANCH="${{ secrets.SYNC_TARGET_BASE_BRANCH_NAME }}" SOURCE_BRANCH="${{ env.SOURCE_BRANCH_NAME }}" - PR_TITLE="${{secrets.SYNC_PR_TITLE}}" git checkout $SOURCE_BRANCH git remote add target-origin "https://$GH_TOKEN@github.com/$TARGET_REPO.git" - git push target-origin $SOURCE_BRANCH:$TARGET_BRANCH - - - gh pr create \ - --base $TARGET_BASE_BRANCH \ - --head $TARGET_BRANCH \ - --title "sync changes" \ - --body "sync changes" \ - --repo $TARGET_REPO + git push target-origin $SOURCE_BRANCH:$TARGET_BRANCH \ No newline at end of file From e5902152abd656f5639b9973e5ceefd067a75f80 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:21:03 +0530 Subject: [PATCH 08/24] fix: overflow issues in the profile settings layout (#3163) --- web/pages/profile/activity.tsx | 4 ++-- web/pages/profile/change-password.tsx | 2 +- web/pages/profile/index.tsx | 2 +- web/pages/profile/preferences.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/pages/profile/activity.tsx b/web/pages/profile/activity.tsx index d943f4b9a..c05a9559e 100644 --- a/web/pages/profile/activity.tsx +++ b/web/pages/profile/activity.tsx @@ -25,7 +25,7 @@ const ProfileActivityPage: NextPageWithLayout = () => { const { data: userActivity } = useSWR(USER_ACTIVITY, () => userService.getUserActivity()); return ( -
+

Activity

@@ -180,7 +180,7 @@ const ProfileActivityPage: NextPageWithLayout = () => { ) : ( - + diff --git a/web/pages/profile/change-password.tsx b/web/pages/profile/change-password.tsx index 85074269c..fe78546a5 100644 --- a/web/pages/profile/change-password.tsx +++ b/web/pages/profile/change-password.tsx @@ -90,7 +90,7 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => { return (

Change password

diff --git a/web/pages/profile/index.tsx b/web/pages/profile/index.tsx index dc75ee12b..b7cdd135c 100644 --- a/web/pages/profile/index.tsx +++ b/web/pages/profile/index.tsx @@ -168,7 +168,7 @@ const ProfileSettingsPage: NextPageWithLayout = () => { )} /> setDeactivateAccountModal(false)} /> -
+
diff --git a/web/pages/profile/preferences.tsx b/web/pages/profile/preferences.tsx index 9bf525a17..97b920eda 100644 --- a/web/pages/profile/preferences.tsx +++ b/web/pages/profile/preferences.tsx @@ -49,7 +49,7 @@ const ProfilePreferencesPage: NextPageWithLayout = observer(() => { return ( <> {currentUser ? ( -
+

Preferences

From d473ba9d0d9a9e19f85865573dc3fefb23d444eb Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:32:00 +0530 Subject: [PATCH 09/24] fix: create issue project ID (#3131) --- web/components/issues/form.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index bc325d95e..c0d1ebc5c 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -227,6 +227,7 @@ export const IssueForm: FC = observer((props) => { reset({ ...defaultValues, ...initialData, + project: projectId, }); }, [setFocus, initialData, reset]); From 3d83101f691fee18abb0aaade8ebc91f221f3a95 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Sun, 17 Dec 2023 00:34:42 +0530 Subject: [PATCH 10/24] fix: issue with `cycle` and `module` sidebar filter implementation. (#3142) --- .../core/sidebar/sidebar-progress-stats.tsx | 42 ++++++++----------- web/components/cycles/sidebar.tsx | 28 ++++++++++++- web/components/modules/sidebar.tsx | 27 +++++++++++- 3 files changed, 68 insertions(+), 29 deletions(-) diff --git a/web/components/core/sidebar/sidebar-progress-stats.tsx b/web/components/core/sidebar/sidebar-progress-stats.tsx index ac4ffcdc5..8cea3784f 100644 --- a/web/components/core/sidebar/sidebar-progress-stats.tsx +++ b/web/components/core/sidebar/sidebar-progress-stats.tsx @@ -14,6 +14,7 @@ import { SingleProgressStats } from "components/core"; import { Avatar, StateGroupIcon } from "@plane/ui"; // types import { + IIssueFilterOptions, IModule, TAssigneesDistribution, TCompletionChartDistribution, @@ -35,6 +36,9 @@ type Props = { roundedTab?: boolean; noBackground?: boolean; isPeekView?: boolean; + isCompleted?: boolean; + filters?: IIssueFilterOptions; + handleFiltersUpdate: (key: keyof IIssueFilterOptions, value: string | string[]) => void; }; export const SidebarProgressStats: React.FC = ({ @@ -44,7 +48,10 @@ export const SidebarProgressStats: React.FC = ({ module, roundedTab, noBackground, + isCompleted = false, isPeekView = false, + filters, + handleFiltersUpdate, }) => { const { storedValue: tab, setValue: setTab } = useLocalStorage("tab", "Assignees"); @@ -140,20 +147,11 @@ export const SidebarProgressStats: React.FC = ({ } completed={assignee.completed_issues} total={assignee.total_issues} - {...(!isPeekView && { - onClick: () => { - // TODO: set filters here - // if (filters?.assignees?.includes(assignee.assignee_id ?? "")) - // setFilters({ - // assignees: filters?.assignees?.filter((a) => a !== assignee.assignee_id), - // }); - // else - // setFilters({ - // assignees: [...(filters?.assignees ?? []), assignee.assignee_id ?? ""], - // }); - }, - // selected: filters?.assignees?.includes(assignee.assignee_id ?? ""), - })} + {...(!isPeekView && + !isCompleted && { + onClick: () => handleFiltersUpdate("assignees", assignee.assignee_id ?? ""), + selected: filters?.assignees?.includes(assignee.assignee_id ?? ""), + })} /> ); else @@ -200,17 +198,11 @@ export const SidebarProgressStats: React.FC = ({ } completed={label.completed_issues} total={label.total_issues} - {...(!isPeekView && { - // TODO: set filters here - onClick: () => { - // if (filters.labels?.includes(label.label_id ?? "")) - // setFilters({ - // labels: filters?.labels?.filter((l) => l !== label.label_id), - // }); - // else setFilters({ labels: [...(filters?.labels ?? []), label.label_id ?? ""] }); - }, - // selected: filters?.labels?.includes(label.label_id ?? ""), - })} + {...(!isPeekView && + !isCompleted && { + onClick: () => handleFiltersUpdate("labels", label.label_id ?? ""), + selected: filters?.labels?.includes(label.label_id ?? `no-label-${index}`), + })} /> )) ) : ( diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index fa7fef008..1fd1cd05c 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useForm } from "react-hook-form"; @@ -29,7 +29,8 @@ import { renderShortMonthDate, } from "helpers/date-time.helper"; // types -import { ICycle } from "types"; +import { ICycle, IIssueFilterOptions } from "types"; +import { EFilterType } from "store/issues/types"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; // fetch-keys @@ -54,6 +55,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const { cycle: cycleDetailsStore, + cycleIssuesFilter: { issueFilters, updateFilters }, trackEvent: { setTrackElement }, user: { currentProjectRole }, } = useMobxStore(); @@ -245,6 +247,25 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { } }; + const handleFiltersUpdate = useCallback( + (key: keyof IIssueFilterOptions, value: string | string[]) => { + if (!workspaceSlug || !projectId) return; + const newValues = issueFilters?.filters?.[key] ?? []; + + if (Array.isArray(value)) { + value.forEach((val) => { + if (!newValues.includes(val)) newValues.push(val); + }); + } else { + if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); + else newValues.push(value); + } + + updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { [key]: newValues }, cycleId); + }, + [workspaceSlug, projectId, cycleId, issueFilters, updateFilters] + ); + const cycleStatus = cycleDetails?.start_date && cycleDetails?.end_date ? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date) @@ -538,6 +559,9 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { }} totalIssues={cycleDetails.total_issues} isPeekView={Boolean(peekCycle)} + isCompleted={isCompleted} + filters={issueFilters?.filters} + handleFiltersUpdate={handleFiltersUpdate} />
)} diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index f2db5dbd5..35bf88567 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; @@ -25,7 +25,8 @@ import { } from "helpers/date-time.helper"; import { copyUrlToClipboard } from "helpers/string.helper"; // types -import { ILinkDetails, IModule, ModuleLink } from "types"; +import { IIssueFilterOptions, ILinkDetails, IModule, ModuleLink } from "types"; +import { EFilterType } from "store/issues/types"; // constant import { MODULE_STATUS } from "constants/module"; import { EUserWorkspaceRoles } from "constants/workspace"; @@ -62,6 +63,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { updateModuleLink, deleteModuleLink, }, + moduleIssuesFilter: { issueFilters, updateFilters }, user: userStore, } = useMobxStore(); @@ -211,6 +213,25 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { } }; + const handleFiltersUpdate = useCallback( + (key: keyof IIssueFilterOptions, value: string | string[]) => { + if (!workspaceSlug || !projectId) return; + const newValues = issueFilters?.filters?.[key] ?? []; + + if (Array.isArray(value)) { + value.forEach((val) => { + if (!newValues.includes(val)) newValues.push(val); + }); + } else { + if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); + else newValues.push(value); + } + + updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { [key]: newValues }, moduleId); + }, + [workspaceSlug, projectId, moduleId, issueFilters, updateFilters] + ); + useEffect(() => { if (moduleDetails) reset({ @@ -544,6 +565,8 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { totalIssues={moduleDetails.total_issues} module={moduleDetails} isPeekView={Boolean(peekModule)} + filters={issueFilters?.filters} + handleFiltersUpdate={handleFiltersUpdate} />
)} From 8a1a6c6f62f30607046c85e5bace9e889cd4b55a Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:35:53 +0530 Subject: [PATCH 11/24] chore: disable API calls for un-authorized users (#3144) * fix: disable api calls if user doesn't have access * chore: remove permissions from component level * chore: rmeove console log and organize import statements --- .../core/modals/bulk-delete-issues-modal.tsx | 30 +++++---- .../roots/archived-issue-layout-root.tsx | 6 +- .../issue-layouts/roots/cycle-layout-root.tsx | 21 +++--- .../roots/draft-issue-layout-root.tsx | 6 +- .../roots/module-layout-root.tsx | 21 +++--- .../roots/project-layout-root.tsx | 6 +- .../roots/project-view-layout-root.tsx | 10 +-- web/layouts/auth-layout/project-wrapper.tsx | 65 +++++++++++++------ web/layouts/auth-layout/user-wrapper.tsx | 3 +- web/layouts/auth-layout/workspace-wrapper.tsx | 18 ++--- web/store/archived-issues/issue.store.ts | 2 + web/store/cycle/cycle_issue.store.ts | 6 +- web/store/issue/issue.store.ts | 6 +- .../project-issues/archived/issue.store.ts | 3 +- .../project-issues/cycle/issue.store.ts | 3 +- .../project-issues/draft/issue.store.ts | 3 +- .../project-issues/module/issue.store.ts | 3 +- .../project-view/issue.store.ts | 3 +- .../project-issues/project/issue.store.ts | 3 +- web/store/module/module_issue.store.ts | 6 +- .../profile-issues/issue_filters.store.ts | 1 - 21 files changed, 139 insertions(+), 86 deletions(-) diff --git a/web/components/core/modals/bulk-delete-issues-modal.tsx b/web/components/core/modals/bulk-delete-issues-modal.tsx index 42ff147fa..d745e1111 100644 --- a/web/components/core/modals/bulk-delete-issues-modal.tsx +++ b/web/components/core/modals/bulk-delete-issues-modal.tsx @@ -1,14 +1,14 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; -import useSWR from "swr"; -// react hook form +import { observer } from "mobx-react-lite"; import { SubmitHandler, useForm } from "react-hook-form"; -// headless ui import { Combobox, Dialog, Transition } from "@headlessui/react"; +import useSWR from "swr"; +// hooks +import { useMobxStore } from "lib/mobx/store-provider"; +import useToast from "hooks/use-toast"; // services import { IssueService } from "services/issue"; -// hooks -import useToast from "hooks/use-toast"; // ui import { Button, LayersIcon } from "@plane/ui"; // icons @@ -30,17 +30,25 @@ type Props = { const issueService = new IssueService(); -export const BulkDeleteIssuesModal: React.FC = (props) => { +export const BulkDeleteIssuesModal: React.FC = observer((props) => { const { isOpen, onClose } = props; + // states + const [query, setQuery] = useState(""); // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - // states - const [query, setQuery] = useState(""); + // store hooks + const { + user: { hasPermissionToCurrentProject }, + } = useMobxStore(); // fetching project issues. const { data: issues } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, - workspaceSlug && projectId ? () => issueService.getIssues(workspaceSlug as string, projectId as string) : null + workspaceSlug && projectId && hasPermissionToCurrentProject + ? PROJECT_ISSUES_LIST(workspaceSlug.toString(), projectId.toString()) + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => issueService.getIssues(workspaceSlug.toString(), projectId.toString()) + : null ); const { setToastAlert } = useToast(); @@ -222,4 +230,4 @@ export const BulkDeleteIssuesModal: React.FC = (props) => { ); -}; +}); diff --git a/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx index fd5002b17..53171f4e5 100644 --- a/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx @@ -9,7 +9,7 @@ import { ArchivedIssueListLayout, ArchivedIssueAppliedFiltersRoot } from "compon export const ArchivedIssueLayoutRoot: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + const { workspaceSlug, projectId } = router.query; const { projectArchivedIssues: { getIssues, fetchIssues }, @@ -18,8 +18,8 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => { useSWR(workspaceSlug && projectId ? `ARCHIVED_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => { if (workspaceSlug && projectId) { - await fetchFilters(workspaceSlug, projectId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader"); + await fetchFilters(workspaceSlug.toString(), projectId.toString()); + await fetchIssues(workspaceSlug.toString(), projectId.toString(), getIssues ? "mutation" : "init-loader"); } }); diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index 0d40a7f06..f967956f0 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -24,11 +24,7 @@ export const CycleLayoutRoot: React.FC = observer(() => { const [transferIssuesModal, setTransferIssuesModal] = useState(false); const router = useRouter(); - const { workspaceSlug, projectId, cycleId } = router.query as { - workspaceSlug: string; - projectId: string; - cycleId: string; - }; + const { workspaceSlug, projectId, cycleId } = router.query; const { cycle: cycleStore, @@ -40,8 +36,13 @@ export const CycleLayoutRoot: React.FC = observer(() => { workspaceSlug && projectId && cycleId ? `CYCLE_ISSUES_V3_${workspaceSlug}_${projectId}_${cycleId}` : null, async () => { if (workspaceSlug && projectId && cycleId) { - await fetchFilters(workspaceSlug, projectId, cycleId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader", cycleId); + await fetchFilters(workspaceSlug.toString(), projectId.toString(), cycleId.toString()); + await fetchIssues( + workspaceSlug.toString(), + projectId.toString(), + getIssues ? "mutation" : "init-loader", + cycleId.toString() + ); } } ); @@ -69,7 +70,11 @@ export const CycleLayoutRoot: React.FC = observer(() => { ) : ( <> {Object.keys(getIssues ?? {}).length == 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx index cf7939015..d09d47714 100644 --- a/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/draft-issue-layout-root.tsx @@ -11,7 +11,7 @@ import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root"; export const DraftIssueLayoutRoot: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + const { workspaceSlug, projectId } = router.query; const { projectDraftIssuesFilter: { issueFilters, fetchFilters }, @@ -20,8 +20,8 @@ export const DraftIssueLayoutRoot: React.FC = observer(() => { useSWR(workspaceSlug && projectId ? `DRAFT_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => { if (workspaceSlug && projectId) { - await fetchFilters(workspaceSlug, projectId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader"); + await fetchFilters(workspaceSlug.toString(), projectId.toString()); + await fetchIssues(workspaceSlug.toString(), projectId.toString(), getIssues ? "mutation" : "init-loader"); } }); diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index bbf1ec2da..21f52564e 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -20,11 +20,7 @@ import { Spinner } from "@plane/ui"; export const ModuleLayoutRoot: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId, moduleId } = router.query as { - workspaceSlug: string; - projectId: string; - moduleId: string; - }; + const { workspaceSlug, projectId, moduleId } = router.query; const { moduleIssues: { loader, getIssues, fetchIssues }, @@ -35,8 +31,13 @@ export const ModuleLayoutRoot: React.FC = observer(() => { workspaceSlug && projectId && moduleId ? `MODULE_ISSUES_V3_${workspaceSlug}_${projectId}_${moduleId}` : null, async () => { if (workspaceSlug && projectId && moduleId) { - await fetchFilters(workspaceSlug, projectId, moduleId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader", moduleId); + await fetchFilters(workspaceSlug.toString(), projectId.toString(), moduleId.toString()); + await fetchIssues( + workspaceSlug.toString(), + projectId.toString(), + getIssues ? "mutation" : "init-loader", + moduleId.toString() + ); } } ); @@ -54,7 +55,11 @@ export const ModuleLayoutRoot: React.FC = observer(() => { ) : ( <> {Object.keys(getIssues ?? {}).length == 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index 9722c4760..db30e4b7c 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -19,7 +19,7 @@ import { Spinner } from "@plane/ui"; export const ProjectLayoutRoot: React.FC = observer(() => { // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + const { workspaceSlug, projectId } = router.query; const { projectIssues: { loader, getIssues, fetchIssues }, @@ -28,8 +28,8 @@ export const ProjectLayoutRoot: React.FC = observer(() => { useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => { if (workspaceSlug && projectId) { - await fetchFilters(workspaceSlug, projectId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader"); + await fetchFilters(workspaceSlug.toString(), projectId.toString()); + await fetchIssues(workspaceSlug.toString(), projectId.toString(), getIssues ? "mutation" : "init-loader"); } }); diff --git a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx index da66a493f..b6623f72c 100644 --- a/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-view-layout-root.tsx @@ -18,11 +18,7 @@ import { Spinner } from "@plane/ui"; export const ProjectViewLayoutRoot: React.FC = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId, viewId } = router.query as { - workspaceSlug: string; - projectId: string; - viewId?: string; - }; + const { workspaceSlug, projectId, viewId } = router.query; const { viewIssues: { loader, getIssues, fetchIssues }, @@ -31,8 +27,8 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => { useSWR(workspaceSlug && projectId && viewId ? `PROJECT_ISSUES_V3_${workspaceSlug}_${projectId}` : null, async () => { if (workspaceSlug && projectId && viewId) { - await fetchFilters(workspaceSlug, projectId, viewId); - await fetchIssues(workspaceSlug, projectId, getIssues ? "mutation" : "init-loader"); + await fetchFilters(workspaceSlug.toString(), projectId.toString(), viewId.toString()); + await fetchIssues(workspaceSlug.toString(), projectId.toString(), getIssues ? "mutation" : "init-loader"); } }); diff --git a/web/layouts/auth-layout/project-wrapper.tsx b/web/layouts/auth-layout/project-wrapper.tsx index 5f162b302..c3352e06d 100644 --- a/web/layouts/auth-layout/project-wrapper.tsx +++ b/web/layouts/auth-layout/project-wrapper.tsx @@ -19,7 +19,7 @@ export const ProjectAuthWrapper: FC = observer((props) => { const { children } = props; // store const { - user: { fetchUserProjectInfo, projectMemberInfo, hasPermissionToProject }, + user: { fetchUserProjectInfo, projectMemberInfo, hasPermissionToCurrentProject }, project: { fetchProjectDetails, workspaceProjects }, projectLabel: { fetchProjectLabels }, projectMember: { fetchProjectMembers }, @@ -47,44 +47,67 @@ export const ProjectAuthWrapper: FC = observer((props) => { ); // fetching project labels useSWR( - workspaceSlug && projectId ? `PROJECT_LABELS_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject ? `PROJECT_LABELS_${workspaceSlug}_${projectId}` : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) + : null ); // fetching project members useSWR( - workspaceSlug && projectId ? `PROJECT_MEMBERS_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject + ? `PROJECT_MEMBERS_${workspaceSlug}_${projectId}` + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) + : null ); // fetching project states useSWR( - workspaceSlug && projectId ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject ? `PROJECT_STATES_${workspaceSlug}_${projectId}` : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchProjectStates(workspaceSlug.toString(), projectId.toString()) + : null ); // fetching project estimates useSWR( - workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchProjectEstimates(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject + ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchProjectEstimates(workspaceSlug.toString(), projectId.toString()) + : null ); // fetching project cycles useSWR( - workspaceSlug && projectId ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchCycles(workspaceSlug.toString(), projectId.toString(), "all") : null + workspaceSlug && projectId && hasPermissionToCurrentProject + ? `PROJECT_ALL_CYCLES_${workspaceSlug}_${projectId}` + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchCycles(workspaceSlug.toString(), projectId.toString(), "all") + : null ); // fetching project modules useSWR( - workspaceSlug && projectId ? `PROJECT_MODULES_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchModules(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject + ? `PROJECT_MODULES_${workspaceSlug}_${projectId}` + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchModules(workspaceSlug.toString(), projectId.toString()) + : null ); // fetching project views useSWR( - workspaceSlug && projectId ? `PROJECT_VIEWS_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId ? () => fetchAllViews(workspaceSlug.toString(), projectId.toString()) : null + workspaceSlug && projectId && hasPermissionToCurrentProject ? `PROJECT_VIEWS_${workspaceSlug}_${projectId}` : null, + workspaceSlug && projectId && hasPermissionToCurrentProject + ? () => fetchAllViews(workspaceSlug.toString(), projectId.toString()) + : null ); - // TODO: fetching project pages // fetching project inboxes if inbox is enabled useSWR( - workspaceSlug && projectId && isInboxEnabled ? `PROJECT_INBOXES_${workspaceSlug}_${projectId}` : null, - workspaceSlug && projectId && isInboxEnabled + workspaceSlug && projectId && hasPermissionToCurrentProject && isInboxEnabled + ? `PROJECT_INBOXES_${workspaceSlug}_${projectId}` + : null, + workspaceSlug && projectId && hasPermissionToCurrentProject && isInboxEnabled ? () => fetchInboxesList(workspaceSlug.toString(), projectId.toString()) : null, { @@ -97,7 +120,7 @@ export const ProjectAuthWrapper: FC = observer((props) => { const projectExists = projectId ? projectsList?.find((project) => project.id === projectId.toString()) : null; // check if the project member apis is loading - if (!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null) + if (!projectMemberInfo && projectId && hasPermissionToCurrentProject === null) return (
@@ -107,10 +130,10 @@ export const ProjectAuthWrapper: FC = observer((props) => { ); // check if the user don't have permission to access the project - if (projectExists && projectId && hasPermissionToProject[projectId.toString()] === false) return ; + if (projectExists && projectId && hasPermissionToCurrentProject === false) return ; // check if the project info is not found. - if (!projectExists && projectId && hasPermissionToProject[projectId.toString()] === false) + if (!projectExists && projectId && hasPermissionToCurrentProject === false) return (
= observer((props) => { shouldRetryOnError: false, }); // fetching current user instance admin status - useSWR("CURRENT_USER_INSTANCE_ADMIN_STATUS", () => fetchCurrentUserInstanceAdminStatus(), { + useSWRImmutable("CURRENT_USER_INSTANCE_ADMIN_STATUS", () => fetchCurrentUserInstanceAdminStatus(), { shouldRetryOnError: false, }); // fetching user settings diff --git a/web/layouts/auth-layout/workspace-wrapper.tsx b/web/layouts/auth-layout/workspace-wrapper.tsx index e61dd48f8..c9db36140 100644 --- a/web/layouts/auth-layout/workspace-wrapper.tsx +++ b/web/layouts/auth-layout/workspace-wrapper.tsx @@ -32,23 +32,25 @@ export const WorkspaceAuthWrapper: FC = observer((props) ); // fetching workspace projects useSWR( - workspaceSlug ? `WORKSPACE_PROJECTS_${workspaceSlug}` : null, - workspaceSlug ? () => fetchProjects(workspaceSlug.toString()) : null + workspaceSlug && hasPermissionToCurrentWorkspace ? `WORKSPACE_PROJECTS_${workspaceSlug}` : null, + workspaceSlug && hasPermissionToCurrentWorkspace ? () => fetchProjects(workspaceSlug.toString()) : null ); // fetch workspace members useSWR( - workspaceSlug ? `WORKSPACE_MEMBERS_${workspaceSlug}` : null, - workspaceSlug ? () => fetchWorkspaceMembers(workspaceSlug.toString()) : null + workspaceSlug && hasPermissionToCurrentWorkspace ? `WORKSPACE_MEMBERS_${workspaceSlug}` : null, + workspaceSlug && hasPermissionToCurrentWorkspace ? () => fetchWorkspaceMembers(workspaceSlug.toString()) : null ); // fetch workspace labels useSWR( - workspaceSlug ? `WORKSPACE_LABELS_${workspaceSlug}` : null, - workspaceSlug ? () => fetchWorkspaceLabels(workspaceSlug.toString()) : null + workspaceSlug && hasPermissionToCurrentWorkspace ? `WORKSPACE_LABELS_${workspaceSlug}` : null, + workspaceSlug && hasPermissionToCurrentWorkspace ? () => fetchWorkspaceLabels(workspaceSlug.toString()) : null ); // fetch workspace user projects role useSWR( - workspaceSlug ? `WORKSPACE_PROJECTS_ROLE_${workspaceSlug}` : null, - workspaceSlug ? () => fetchWorkspaceUserProjectsRole(workspaceSlug.toString()) : null + workspaceSlug && hasPermissionToCurrentWorkspace ? `WORKSPACE_PROJECTS_ROLE_${workspaceSlug}` : null, + workspaceSlug && hasPermissionToCurrentWorkspace + ? () => fetchWorkspaceUserProjectsRole(workspaceSlug.toString()) + : null ); // while data is being loaded diff --git a/web/store/archived-issues/issue.store.ts b/web/store/archived-issues/issue.store.ts index 53914cdee..94e7df7af 100644 --- a/web/store/archived-issues/issue.store.ts +++ b/web/store/archived-issues/issue.store.ts @@ -87,10 +87,12 @@ export class ArchivedIssueStore implements IArchivedIssueStore { autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; if ( workspaceSlug && projectId && + hasPermissionToCurrentProject && this.rootStore.archivedIssueFilters.userDisplayFilters && this.rootStore.archivedIssueFilters.userFilters ) diff --git a/web/store/cycle/cycle_issue.store.ts b/web/store/cycle/cycle_issue.store.ts index 4afa35451..860ff02e2 100644 --- a/web/store/cycle/cycle_issue.store.ts +++ b/web/store/cycle/cycle_issue.store.ts @@ -89,10 +89,12 @@ export class CycleIssueStore implements ICycleIssueStore { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; const cycleId = this.rootStore.cycle.cycleId; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; if ( workspaceSlug && projectId && + hasPermissionToCurrentProject && cycleId && this.rootStore.cycleIssueFilter.cycleFilters && this.rootStore.issueFilter.userDisplayFilters @@ -115,8 +117,8 @@ export class CycleIssueStore implements ICycleIssueStore { ? "groupWithSubGroups" : "grouped" : ungroupedLayouts.includes(issueLayout) - ? "ungrouped" - : null; + ? "ungrouped" + : null; return _issueState || null; } diff --git a/web/store/issue/issue.store.ts b/web/store/issue/issue.store.ts index 9b56e0c23..4667b3a65 100644 --- a/web/store/issue/issue.store.ts +++ b/web/store/issue/issue.store.ts @@ -82,9 +82,11 @@ export class IssueStore implements IIssueStore { autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; if ( workspaceSlug && projectId && + hasPermissionToCurrentProject && this.rootStore.issueFilter.userFilters && this.rootStore.issueFilter.userDisplayFilters ) @@ -108,8 +110,8 @@ export class IssueStore implements IIssueStore { : "grouped" : "ungrouped" : ungroupedLayouts.includes(issueLayout) - ? "ungrouped" - : null; + ? "ungrouped" + : null; return _issueState || null; } diff --git a/web/store/issues/project-issues/archived/issue.store.ts b/web/store/issues/project-issues/archived/issue.store.ts index 69a397edd..8449d43b5 100644 --- a/web/store/issues/project-issues/archived/issue.store.ts +++ b/web/store/issues/project-issues/archived/issue.store.ts @@ -60,7 +60,8 @@ export class ProjectArchivedIssuesStore extends IssueBaseStore implements IProje autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; - if (!workspaceSlug || !projectId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject) return; const userFilters = this.rootStore?.projectArchivedIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/issues/project-issues/cycle/issue.store.ts b/web/store/issues/project-issues/cycle/issue.store.ts index 7d5355010..767000902 100644 --- a/web/store/issues/project-issues/cycle/issue.store.ts +++ b/web/store/issues/project-issues/cycle/issue.store.ts @@ -119,7 +119,8 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; const cycleId = this.rootStore.cycle.cycleId; - if (!workspaceSlug || !projectId || !cycleId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject || !cycleId) return; const userFilters = this.rootStore?.cycleIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId); diff --git a/web/store/issues/project-issues/draft/issue.store.ts b/web/store/issues/project-issues/draft/issue.store.ts index ddcebc3f0..31610dad2 100644 --- a/web/store/issues/project-issues/draft/issue.store.ts +++ b/web/store/issues/project-issues/draft/issue.store.ts @@ -63,7 +63,8 @@ export class ProjectDraftIssuesStore extends IssueBaseStore implements IProjectD autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; - if (!workspaceSlug || !projectId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject) return; const userFilters = this.rootStore?.projectDraftIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/issues/project-issues/module/issue.store.ts b/web/store/issues/project-issues/module/issue.store.ts index 8135eacaa..0f7fcae5b 100644 --- a/web/store/issues/project-issues/module/issue.store.ts +++ b/web/store/issues/project-issues/module/issue.store.ts @@ -111,7 +111,8 @@ export class ModuleIssuesStore extends IssueBaseStore implements IModuleIssuesSt const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; const moduleId = this.rootStore.module.moduleId; - if (!workspaceSlug || !projectId || !moduleId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject || !moduleId) return; const userFilters = this.rootStore?.moduleIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation", moduleId); diff --git a/web/store/issues/project-issues/project-view/issue.store.ts b/web/store/issues/project-issues/project-view/issue.store.ts index c1f201be8..c3562078a 100644 --- a/web/store/issues/project-issues/project-view/issue.store.ts +++ b/web/store/issues/project-issues/project-view/issue.store.ts @@ -65,7 +65,8 @@ export class ViewIssuesStore extends IssueBaseStore implements IViewIssuesStore autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; - if (!workspaceSlug || !projectId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject) return; const userFilters = this.rootStore?.viewIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/issues/project-issues/project/issue.store.ts b/web/store/issues/project-issues/project/issue.store.ts index 9b1b83801..8b8075179 100644 --- a/web/store/issues/project-issues/project/issue.store.ts +++ b/web/store/issues/project-issues/project/issue.store.ts @@ -65,7 +65,8 @@ export class ProjectIssuesStore extends IssueBaseStore implements IProjectIssues autorun(() => { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; - if (!workspaceSlug || !projectId) return; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; + if (!workspaceSlug || !projectId || !hasPermissionToCurrentProject) return; const userFilters = this.rootStore?.projectIssuesFilter?.issueFilters?.filters; if (userFilters) this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/module/module_issue.store.ts b/web/store/module/module_issue.store.ts index dde4670ad..43f2e2d6d 100644 --- a/web/store/module/module_issue.store.ts +++ b/web/store/module/module_issue.store.ts @@ -91,10 +91,12 @@ export class ModuleIssueStore implements IModuleIssueStore { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const projectId = this.rootStore.project.projectId; const moduleId = this.rootStore.module.moduleId; + const hasPermissionToCurrentProject = this.rootStore.user.hasPermissionToCurrentProject; if ( workspaceSlug && projectId && + hasPermissionToCurrentProject && moduleId && this.rootStore.moduleFilter.moduleFilters && this.rootStore.issueFilter.userDisplayFilters @@ -117,8 +119,8 @@ export class ModuleIssueStore implements IModuleIssueStore { ? "groupWithSubGroups" : "grouped" : ungroupedLayouts.includes(issueLayout) - ? "ungrouped" - : null; + ? "ungrouped" + : null; return _issueState || null; } diff --git a/web/store/profile-issues/issue_filters.store.ts b/web/store/profile-issues/issue_filters.store.ts index 6573ec447..9f611b39f 100644 --- a/web/store/profile-issues/issue_filters.store.ts +++ b/web/store/profile-issues/issue_filters.store.ts @@ -68,7 +68,6 @@ export class ProfileIssueFilterStore implements IProfileIssueFilterStore { const workspaceSlug = this.rootStore.workspace.workspaceSlug; const userId = this.rootStore.profileIssues?.userId; if (workspaceSlug && userId && this.rootStore.profileIssues.currentProfileTab && this.appliedFilters) { - console.log("autorun triggered"); this.rootStore.profileIssues.fetchIssues( workspaceSlug, userId, From 31fdaf2659c03ed87c2226c5658f751f96880cd3 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:36:56 +0530 Subject: [PATCH 12/24] fix: project members fetch fix (#3145) * chore: all issues assignee fetch fix * chore: assignee fetch logic updated --- .../issues/issue-layouts/properties/assignee.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/components/issues/issue-layouts/properties/assignee.tsx b/web/components/issues/issue-layouts/properties/assignee.tsx index 126c984bd..acebed498 100644 --- a/web/components/issues/issue-layouts/properties/assignee.tsx +++ b/web/components/issues/issue-layouts/properties/assignee.tsx @@ -42,7 +42,7 @@ export const IssuePropertyAssignee: React.FC = observer( // store const { workspace: workspaceStore, - projectMember: { projectMembers: _projectMembers, fetchProjectMembers }, + projectMember: { members: _members, fetchProjectMembers }, } = useMobxStore(); const workspaceSlug = workspaceStore?.workspaceSlug; // states @@ -51,14 +51,14 @@ export const IssuePropertyAssignee: React.FC = observer( const [popperElement, setPopperElement] = useState(null); const [isLoading, setIsLoading] = useState(false); - const getWorkspaceMembers = () => { + const getProjectMembers = () => { setIsLoading(true); if (workspaceSlug && projectId) fetchProjectMembers(workspaceSlug, projectId).then(() => setIsLoading(false)); }; const updatedDefaultOptions: IProjectMember[] = defaultOptions.map((member: any) => ({ member: { ...member } })) ?? []; - const projectMembers = _projectMembers ?? updatedDefaultOptions; + const projectMembers = projectId && _members[projectId] ? _members[projectId] : updatedDefaultOptions; const options = projectMembers?.map((member) => ({ value: member.member.id, @@ -142,7 +142,7 @@ export const IssuePropertyAssignee: React.FC = observer( className={`flex w-full items-center justify-between gap-1 text-xs ${ disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer" } ${buttonClassName}`} - onClick={() => !projectMembers && getWorkspaceMembers()} + onClick={() => (!projectId || !_members[projectId]) && getProjectMembers()} > {label} {!hideDropdownArrow && !disabled &&

diff --git a/web/components/issues/attachment/attachments.tsx b/web/components/issues/attachment/attachments.tsx index 86cafd7a9..1b4915579 100644 --- a/web/components/issues/attachment/attachments.tsx +++ b/web/components/issues/attachment/attachments.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import React, { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import useSWR from "swr"; @@ -24,7 +24,14 @@ import { IIssueAttachment } from "types"; const issueAttachmentService = new IssueAttachmentService(); const projectMemberService = new ProjectMemberService(); -export const IssueAttachments = () => { +type Props = { + editable: boolean; +}; + +export const IssueAttachments: React.FC = (props) => { + const { editable } = props; + + // states const [deleteAttachment, setDeleteAttachment] = useState(null); const [attachmentDeleteModal, setAttachmentDeleteModal] = useState(false); @@ -86,14 +93,16 @@ export const IssueAttachments = () => {

- + {editable && ( + + )}
))} diff --git a/web/components/issues/description-form.tsx b/web/components/issues/description-form.tsx index b1e455173..677ab5e22 100644 --- a/web/components/issues/description-form.tsx +++ b/web/components/issues/description-form.tsx @@ -135,7 +135,9 @@ export const IssueDescriptionForm: FC = (props) => { debouncedFormSave(); }} required - className="min-h-min block w-full resize-none overflow-hidden rounded border-none bg-transparent px-3 py-2 text-2xl font-medium outline-none ring-0 focus:ring-1 focus:ring-custom-primary" + className={`min-h-min block w-full resize-none overflow-hidden rounded border-none bg-transparent px-3 py-2 text-2xl font-medium outline-none ring-0 focus:ring-1 focus:ring-custom-primary ${ + !isAllowed ? "hover:cursor-not-allowed" : "" + }`} hasError={Boolean(errors?.description)} role="textbox" disabled={!isAllowed} @@ -170,7 +172,9 @@ export const IssueDescriptionForm: FC = (props) => { setShouldShowAlert={setShowAlert} setIsSubmitting={setIsSubmitting} dragDropEnabled - customClassName={isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200"} + customClassName={ + isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200 pointer-events-none" + } noBorder={!isAllowed} onChange={(description: Object, description_html: string) => { setShowAlert(true); diff --git a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx index 98327edab..c2532b802 100644 --- a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -10,6 +10,9 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector"; // types import { IIssue } from "types"; import { IIssueResponse } from "store/issues/types"; +import { useMobxStore } from "lib/mobx/store-provider"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; type Props = { issues: IIssueResponse | undefined; @@ -26,6 +29,11 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { // states const [isMenuActive, setIsMenuActive] = useState(false); + // mobx store + const { + user: { currentProjectRole }, + } = useMobxStore(); + const menuActionRef = useRef(null); const handleIssuePeekOverview = (issue: IIssue) => { @@ -51,6 +59,8 @@ export const CalendarIssueBlocks: React.FC = observer((props) => {
); + const isEditable = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; + return ( <> {issueIdList?.slice(0, showAllIssues ? issueIdList.length : 4).map((issueId, index) => { @@ -58,7 +68,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { const issue = issues?.[issueId]; return ( - + {(provided, snapshot) => (
= (props) => { return ( <> - + {(provided, snapshot) => (
= observer((props) => { - const { issueDetails, submitChanges, uneditable = false } = props; + const { issueDetails, submitChanges, uneditable } = props; // states const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); // router @@ -152,7 +152,9 @@ export const IssueMainContent: React.FC = observer((props) => { ); }; - const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; + const isAllowed = + (!!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER) || + (uneditable !== undefined && !uneditable); return ( <> @@ -232,7 +234,7 @@ export const IssueMainContent: React.FC = observer((props) => { workspaceSlug={workspaceSlug as string} issue={issueDetails} handleFormSubmit={submitChanges} - isAllowed={isAllowed || !uneditable} + isAllowed={isAllowed} /> {workspaceSlug && projectId && ( @@ -250,8 +252,8 @@ export const IssueMainContent: React.FC = observer((props) => {

Attachments

- - + +
@@ -264,7 +266,7 @@ export const IssueMainContent: React.FC = observer((props) => { />
diff --git a/web/components/issues/peek-overview/issue-detail.tsx b/web/components/issues/peek-overview/issue-detail.tsx index 610d0348d..d8a88cff7 100644 --- a/web/components/issues/peek-overview/issue-detail.tsx +++ b/web/components/issues/peek-overview/issue-detail.tsx @@ -153,10 +153,12 @@ export const PeekOverviewIssueDetails: FC = (props) = debouncedFormSave(); }} required={true} - className="min-h-10 block w-full resize-none overflow-hidden rounded border-none bg-transparent !p-0 text-xl outline-none ring-0 focus:!px-3 focus:!py-2 focus:ring-1 focus:ring-custom-primary" + className={`min-h-10 block w-full resize-none overflow-hidden rounded border-none bg-transparent !p-0 text-xl outline-none ring-0 focus:!px-3 focus:!py-2 focus:ring-1 focus:ring-custom-primary ${ + !isAllowed ? "hover:cursor-not-allowed" : "" + }`} hasError={Boolean(errors?.description)} role="textbox" - disabled={!true} + disabled={!isAllowed} /> )} /> @@ -188,7 +190,9 @@ export const PeekOverviewIssueDetails: FC = (props) = setShouldShowAlert={setShowAlert} setIsSubmitting={setIsSubmitting} dragDropEnabled - customClassName={isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200"} + customClassName={ + isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200 pointer-events-none" + } noBorder={!isAllowed} onChange={(description: Object, description_html: string) => { setShowAlert(true); From a37dec45d94569a0c52696c6dcfbfc5b39148f2f Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:39:23 +0530 Subject: [PATCH 14/24] chore: made project-identifier rule consistent (#3148) --- web/components/project/create-project-modal.tsx | 4 ++-- web/components/project/form.tsx | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index e8a85600f..83c7851b9 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -308,8 +308,8 @@ export const CreateProjectModal: FC = observer((props) => { message: "Identifier must at least be of 1 character", }, maxLength: { - value: 12, - message: "Identifier must at most be of 12 characters", + value: 6, + message: "Identifier must at most be of 6 characters", }, }} render={({ field: { value, onChange } }) => ( diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index f4d753885..9af8c7831 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -232,7 +232,7 @@ export const ProjectDetailsForm: FC = (props) => { />
-
+

Identifier

= (props) => { message: "Identifier must at least be of 1 character", }, maxLength: { - value: 12, - message: "Identifier must at most be of 5 characters", + value: 6, + message: "Identifier must at most be of 6 characters", }, }} render={({ field: { value, ref } }) => ( @@ -265,6 +265,7 @@ export const ProjectDetailsForm: FC = (props) => { /> )} /> + {errors?.identifier?.message}
From 969a51f425a29026586c19ba0a80415db10191b5 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 18 Dec 2023 12:11:14 +0530 Subject: [PATCH 15/24] chore: issue click & peek overview improvement (#3157) * improve issue popover to detect outside click * chore: stopPropagation event added to prevent peekoverview triggering in action menu & issue properties * chore: stopPropagation event added to prevent peekoverview triggering in issue properties * chore: enable entire issue card clickability in list and kanban layout, introduce control-click functionality to open issues in new tabs * chore: build error fix and unused variable removed * chore: build error fix --------- Co-authored-by: sriram veeraghanta --- .../issue-layouts/calendar/issue-blocks.tsx | 18 +++-- .../issues/issue-layouts/gantt/blocks.tsx | 16 ++-- .../issues/issue-layouts/kanban/block.tsx | 79 +++++++++++-------- .../issues/issue-layouts/list/block.tsx | 28 ++++--- .../issue-layouts/properties/assignee.tsx | 6 +- .../issues/issue-layouts/properties/date.tsx | 5 +- .../issue-layouts/properties/estimates.tsx | 2 + .../issue-layouts/properties/labels.tsx | 6 +- .../issues/issue-layouts/properties/state.tsx | 6 +- .../quick-action-dropdowns/all-issue.tsx | 7 +- .../quick-action-dropdowns/archived-issue.tsx | 7 +- .../quick-action-dropdowns/cycle-issue.tsx | 7 +- .../quick-action-dropdowns/module-issue.tsx | 8 +- .../quick-action-dropdowns/project-issue.tsx | 7 +- .../columns/issue/issue-column.tsx | 18 +++-- .../spreadsheet/columns/state-column.tsx | 2 - web/components/issues/peek-overview/view.tsx | 9 ++- web/components/issues/sub-issues/issue.tsx | 16 ++-- web/components/project/priority-select.tsx | 2 + 19 files changed, 167 insertions(+), 82 deletions(-) diff --git a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx index c2532b802..f8eead33f 100644 --- a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -36,13 +36,17 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { const menuActionRef = useRef(null); - const handleIssuePeekOverview = (issue: IIssue) => { + const handleIssuePeekOverview = (issue: IIssue, event: React.MouseEvent) => { const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, - }); + if (event.ctrlKey || event.metaKey) { + const issueUrl = `/${issue.workspace_detail.slug}/projects/${issue.project_detail.id}/issues/${issue?.id}`; + window.open(issueUrl, "_blank"); // Open link in a new tab + } else { + router.push({ + pathname: router.pathname, + query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, + }); + } }; useOutsideClickDetector(menuActionRef, () => setIsMenuActive(false)); @@ -75,7 +79,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef} - onClick={() => handleIssuePeekOverview(issue)} + onClick={(e) => handleIssuePeekOverview(issue, e)} > {issue?.tempId !== undefined && (
diff --git a/web/components/issues/issue-layouts/gantt/blocks.tsx b/web/components/issues/issue-layouts/gantt/blocks.tsx index 916dceedf..41085978a 100644 --- a/web/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/components/issues/issue-layouts/gantt/blocks.tsx @@ -9,13 +9,17 @@ import { IIssue } from "types"; export const IssueGanttBlock = ({ data }: { data: IIssue }) => { const router = useRouter(); - const handleIssuePeekOverview = () => { + const handleIssuePeekOverview = (event: React.MouseEvent) => { const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssueId: data?.id, peekProjectId: data?.project }, - }); + if (event.ctrlKey || event.metaKey) { + const issueUrl = `/${data?.workspace_detail.slug}/projects/${data?.project_detail.id}/issues/${data?.id}`; + window.open(issueUrl, "_blank"); // Open link in a new tab + } else { + router.push({ + pathname: router.pathname, + query: { ...query, peekIssueId: data?.id, peekProjectId: data?.project }, + }); + } }; return ( diff --git a/web/components/issues/issue-layouts/kanban/block.tsx b/web/components/issues/issue-layouts/kanban/block.tsx index 065b409a6..b48698fa7 100644 --- a/web/components/issues/issue-layouts/kanban/block.tsx +++ b/web/components/issues/issue-layouts/kanban/block.tsx @@ -1,5 +1,5 @@ import { memo } from "react"; -import { Draggable } from "@hello-pangea/dnd"; +import { Draggable, DraggableStateSnapshot } from "@hello-pangea/dnd"; import isEqual from "lodash/isEqual"; // components import { KanBanProperties } from "./properties"; @@ -32,11 +32,23 @@ interface IssueDetailsBlockProps { quickActions: (sub_group_by: string | null, group_by: string | null, issue: IIssue) => React.ReactNode; displayProperties: IIssueDisplayProperties | null; isReadOnly: boolean; + snapshot: DraggableStateSnapshot; + isDragDisabled: boolean; } const KanbanIssueDetailsBlock: React.FC = (props) => { - const { sub_group_id, columnId, issue, showEmptyGroup, handleIssues, quickActions, displayProperties, isReadOnly } = - props; + const { + sub_group_id, + columnId, + issue, + showEmptyGroup, + handleIssues, + quickActions, + displayProperties, + isReadOnly, + snapshot, + isDragDisabled, + } = props; const router = useRouter(); @@ -44,20 +56,29 @@ const KanbanIssueDetailsBlock: React.FC = (props) => { if (issueToUpdate) handleIssues(sub_group_by, group_by, issueToUpdate, EIssueActions.UPDATE); }; - const handleIssuePeekOverview = () => { + const handleIssuePeekOverview = (event: React.MouseEvent) => { const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, - }); + if (event.ctrlKey || event.metaKey) { + const issueUrl = `/${issue.workspace_detail.slug}/projects/${issue.project_detail.id}/issues/${issue?.id}`; + window.open(issueUrl, "_blank"); // Open link in a new tab + } else { + router.push({ + pathname: router.pathname, + query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, + }); + } }; return ( - <> +
{displayProperties && displayProperties?.key && ( -
-
+
+
{issue.project_detail.identifier}-{issue.sequence_id}
@@ -70,9 +91,7 @@ const KanbanIssueDetailsBlock: React.FC = (props) => {
)} -
- {issue.name} -
+
{issue.name}
= (props) => { isReadOnly={isReadOnly} />
- +
); }; @@ -132,22 +151,18 @@ export const KanbanIssueBlock: React.FC = (props) => { {issue.tempId !== undefined && (
)} -
- -
+
)} diff --git a/web/components/issues/issue-layouts/list/block.tsx b/web/components/issues/issue-layouts/list/block.tsx index 0667a7fa9..562f599ab 100644 --- a/web/components/issues/issue-layouts/list/block.tsx +++ b/web/components/issues/issue-layouts/list/block.tsx @@ -25,20 +25,27 @@ export const IssueBlock: React.FC = (props) => { handleIssues(issueToUpdate, EIssueActions.UPDATE); }; - const handleIssuePeekOverview = () => { + const handleIssuePeekOverview = (event: React.MouseEvent) => { const { query } = router; - - router.push({ - pathname: router.pathname, - query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, - }); + if (event.ctrlKey || event.metaKey) { + const issueUrl = `/${issue.workspace_detail.slug}/projects/${issue.project_detail.id}/issues/${issue?.id}`; + window.open(issueUrl, "_blank"); // Open link in a new tab + } else { + router.push({ + pathname: router.pathname, + query: { ...query, peekIssueId: issue?.id, peekProjectId: issue?.project }, + }); + } }; const canEditIssueProperties = canEditProperties(issue.project); return ( <> -
+ ); }; diff --git a/web/components/issues/issue-layouts/properties/assignee.tsx b/web/components/issues/issue-layouts/properties/assignee.tsx index acebed498..01dec9b83 100644 --- a/web/components/issues/issue-layouts/properties/assignee.tsx +++ b/web/components/issues/issue-layouts/properties/assignee.tsx @@ -142,7 +142,10 @@ export const IssuePropertyAssignee: React.FC = observer( className={`flex w-full items-center justify-between gap-1 text-xs ${ disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer" } ${buttonClassName}`} - onClick={() => (!projectId || !_members[projectId]) && getProjectMembers()} + onClick={(e) => { + e.stopPropagation(); + (!projectId || !_members[projectId]) && getProjectMembers(); + }} > {label} {!hideDropdownArrow && !disabled &&
diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index 2f300c990..ff378e23d 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -93,13 +93,17 @@ export const WorkspaceDashboardView = observer(() => { direction: "right", description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", }} - primaryButton={{ - text: "Build your first project", - onClick: () => { - setTrackElement("DASHBOARD_PAGE"); - commandPaletteStore.toggleCreateProjectModal(true); - }, - }} + primaryButton={ + isEditingAllowed + ? { + text: "Build your first project", + onClick: () => { + setTrackElement("DASHBOARD_PAGE"); + commandPaletteStore.toggleCreateProjectModal(true); + }, + } + : null + } disabled={!isEditingAllowed} /> ) diff --git a/web/components/pages/pages-list/list-view.tsx b/web/components/pages/pages-list/list-view.tsx index 9f94a6671..bb35edfa0 100644 --- a/web/components/pages/pages-list/list-view.tsx +++ b/web/components/pages/pages-list/list-view.tsx @@ -58,11 +58,15 @@ export const PagesListView: FC = observer(({ pages }) => { "We wrote Parth and Meera’s love story. You could write your project’s mission, goals, and eventual vision.", direction: "right", }} - primaryButton={{ - icon: , - text: "Create your first page", - onClick: () => toggleCreatePageModal(true), - }} + primaryButton={ + isEditingAllowed + ? { + icon: , + text: "Create your first page", + onClick: () => toggleCreatePageModal(true), + } + : null + } disabled={!isEditingAllowed} /> )} diff --git a/web/components/pages/pages-list/recent-pages-list.tsx b/web/components/pages/pages-list/recent-pages-list.tsx index 21fb8277f..4648ec1e4 100644 --- a/web/components/pages/pages-list/recent-pages-list.tsx +++ b/web/components/pages/pages-list/recent-pages-list.tsx @@ -66,11 +66,15 @@ export const RecentPagesList: FC = observer(() => { "We wrote Parth and Meera’s love story. You could write your project’s mission, goals, and eventual vision.", direction: "right", }} - primaryButton={{ - icon: , - text: "Create your first page", - onClick: () => commandPaletteStore.toggleCreatePageModal(true), - }} + primaryButton={ + isEditingAllowed + ? { + icon: , + text: "Create your first page", + onClick: () => commandPaletteStore.toggleCreatePageModal(true), + } + : null + } disabled={!isEditingAllowed} /> diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 979ade4ec..76f3112b6 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -67,13 +67,17 @@ export const ProjectCardList: FC = observer((props) => { direction: "right", description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.", }} - primaryButton={{ - text: "Start your first project", - onClick: () => { - setTrackElement("PROJECTS_EMPTY_STATE"); - commandPaletteStore.toggleCreateProjectModal(true); - }, - }} + primaryButton={ + isEditingAllowed + ? { + text: "Start your first project", + onClick: () => { + setTrackElement("PROJECTS_EMPTY_STATE"); + commandPaletteStore.toggleCreateProjectModal(true); + }, + } + : null + } disabled={!isEditingAllowed} /> )} diff --git a/web/components/views/views-list.tsx b/web/components/views/views-list.tsx index d293bbe73..d08ec2c04 100644 --- a/web/components/views/views-list.tsx +++ b/web/components/views/views-list.tsx @@ -76,11 +76,15 @@ export const ProjectViewsList = observer(() => { description: "You can create a view from here with as many properties as filters as you see fit.", direction: "right", }} - primaryButton={{ - icon: , - text: "Build your first view", - onClick: () => commandPaletteStore.toggleCreateViewModal(true), - }} + primaryButton={ + isEditingAllowed + ? { + icon: , + text: "Build your first view", + onClick: () => commandPaletteStore.toggleCreateViewModal(true), + } + : null + } disabled={!isEditingAllowed} /> )} diff --git a/web/components/workspace/views/header.tsx b/web/components/workspace/views/header.tsx index b0b67cc22..f0636d80a 100644 --- a/web/components/workspace/views/header.tsx +++ b/web/components/workspace/views/header.tsx @@ -9,7 +9,7 @@ import { CreateUpdateWorkspaceViewModal } from "components/workspace"; // icon import { Plus } from "lucide-react"; // constants -import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace"; +import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "constants/workspace"; export const GlobalViewsHeader: React.FC = observer(() => { const [createViewModal, setCreateViewModal] = useState(false); @@ -17,7 +17,10 @@ export const GlobalViewsHeader: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug, globalViewId } = router.query; - const { globalViews: globalViewsStore } = useMobxStore(); + const { + globalViews: globalViewsStore, + user: { currentWorkspaceRole }, + } = useMobxStore(); // bring the active view to the centre of the header useEffect(() => { @@ -28,11 +31,13 @@ export const GlobalViewsHeader: React.FC = observer(() => { if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" }); }, [globalViewId]); + const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; + const isTabSelected = (tabKey: string) => router.pathname.includes(tabKey); return ( <> setCreateViewModal(false)} /> -
+
{DEFAULT_GLOBAL_VIEWS_LIST.map((tab) => ( { ))} - + {isAuthorizedUser && ( + + )}
); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index c987408b0..2a98c75e4 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -103,13 +103,17 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { description: "A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle.", }} - primaryButton={{ - icon: , - text: "Set your first cycle", - onClick: () => { - setCreateModal(true); - }, - }} + primaryButton={ + isEditingAllowed + ? { + icon: , + text: "Set your first cycle", + onClick: () => { + setCreateModal(true); + }, + } + : null + } disabled={!isEditingAllowed} />
From 0ee6c20272256972bf274c39130a0fa9b736e6c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:26:21 +0530 Subject: [PATCH 20/24] chore(deps): bump cryptography in /apiserver/requirements (#3166) Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.5 to 41.0.6. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/41.0.5...41.0.6) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- apiserver/requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/requirements/base.txt b/apiserver/requirements/base.txt index 6832297e9..0e7a18fa8 100644 --- a/apiserver/requirements/base.txt +++ b/apiserver/requirements/base.txt @@ -30,7 +30,7 @@ openpyxl==3.1.2 beautifulsoup4==4.12.2 dj-database-url==2.1.0 posthog==3.0.2 -cryptography==41.0.5 +cryptography==41.0.6 lxml==4.9.3 boto3==1.28.40 From 18c86bd8cca2314d7a44de2da8eb78718c45041f Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Mon, 18 Dec 2023 13:36:09 +0530 Subject: [PATCH 21/24] chore: add `email from address` in Instance Admin Email Settings. (#3155) --- web/components/instance/email-form.tsx | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/web/components/instance/email-form.tsx b/web/components/instance/email-form.tsx index 60e6c7bf6..82da7553e 100644 --- a/web/components/instance/email-form.tsx +++ b/web/components/instance/email-form.tsx @@ -21,6 +21,7 @@ export interface EmailFormValues { EMAIL_HOST_PASSWORD: string; EMAIL_USE_TLS: string; // EMAIL_USE_SSL: string; + EMAIL_FROM: string; } export const InstanceEmailForm: FC = (props) => { @@ -45,6 +46,7 @@ export const InstanceEmailForm: FC = (props) => { EMAIL_HOST_PASSWORD: config["EMAIL_HOST_PASSWORD"], EMAIL_USE_TLS: config["EMAIL_USE_TLS"], // EMAIL_USE_SSL: config["EMAIL_USE_SSL"], + EMAIL_FROM: config["EMAIL_FROM"], }, }); @@ -168,6 +170,31 @@ export const InstanceEmailForm: FC = (props) => {
+
+
+

From address

+ ( + + )} + /> +

+ You will have to verify your email address to being sending emails. +

+
+
From 37df0bcdd89276bd98f2c08e4a1922835673e244 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Mon, 18 Dec 2023 14:47:40 +0530 Subject: [PATCH 22/24] fix: issue with peek view properties not editable and options not being shown in `all workspace issues`. (#3100) * fix: issue with peek view properties not editable and options not being shown in `all workspace issues`.. * refactor: use projectId from props instead of router query. fix: issue in add to module/ cycle not working properly. --- .../issues/peek-overview/properties.tsx | 20 ++++++++++++++--- web/components/issues/peek-overview/root.tsx | 5 +++-- .../issues/sidebar-select/assignee.tsx | 5 +++-- .../issues/sidebar-select/cycle.tsx | 5 ++++- .../issues/sidebar-select/label.tsx | 5 +++-- .../issues/sidebar-select/module.tsx | 22 ++++++++++++++----- .../issues/sidebar-select/parent.tsx | 5 +++-- .../issues/sidebar-select/state.tsx | 5 +++-- web/components/issues/sidebar.tsx | 6 +++++ 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/web/components/issues/peek-overview/properties.tsx b/web/components/issues/peek-overview/properties.tsx index 25aaf2f88..7f3204196 100644 --- a/web/components/issues/peek-overview/properties.tsx +++ b/web/components/issues/peek-overview/properties.tsx @@ -47,7 +47,7 @@ export const PeekOverviewProperties: FC = observer((pro } = useMobxStore(); const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, peekProjectId: projectId } = router.query; const handleState = (_state: string) => { issueUpdate({ ...issue, state: _state }); @@ -116,7 +116,12 @@ export const PeekOverviewProperties: FC = observer((pro

State

- +
@@ -129,6 +134,7 @@ export const PeekOverviewProperties: FC = observer((pro
@@ -210,7 +216,12 @@ export const PeekOverviewProperties: FC = observer((pro

Parent

- +
@@ -226,6 +237,7 @@ export const PeekOverviewProperties: FC = observer((pro
@@ -240,6 +252,7 @@ export const PeekOverviewProperties: FC = observer((pro
@@ -253,6 +266,7 @@ export const PeekOverviewProperties: FC = observer((pro
= observer((props) => { const { peekIssueId } = router.query; const { - user: { currentProjectRole }, issueDetail: { createIssueComment, updateIssueComment, @@ -58,6 +57,7 @@ export const IssuePeekOverview: FC = observer((props) => { }, archivedIssues: { deleteArchivedIssue }, project: { currentProjectDetails }, + workspaceMember: { currentWorkspaceUserProjectsRole }, } = useMobxStore(); const { setToastAlert } = useToast(); @@ -146,7 +146,8 @@ export const IssuePeekOverview: FC = observer((props) => { } }; - const userRole = currentProjectRole ?? EUserWorkspaceRoles.GUEST; + const userRole = + (currentWorkspaceUserProjectsRole && currentWorkspaceUserProjectsRole[projectId]) ?? EUserWorkspaceRoles.GUEST; return ( diff --git a/web/components/issues/sidebar-select/assignee.tsx b/web/components/issues/sidebar-select/assignee.tsx index 34e3bc06a..dffa46232 100644 --- a/web/components/issues/sidebar-select/assignee.tsx +++ b/web/components/issues/sidebar-select/assignee.tsx @@ -10,6 +10,7 @@ import { PROJECT_MEMBERS } from "constants/fetch-keys"; type Props = { value: string[]; + projectId: string; onChange: (val: string[]) => void; disabled?: boolean; }; @@ -17,9 +18,9 @@ type Props = { // services const projectMemberService = new ProjectMemberService(); -export const SidebarAssigneeSelect: React.FC = ({ value, onChange, disabled = false }) => { +export const SidebarAssigneeSelect: React.FC = ({ value, projectId, onChange, disabled = false }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; const { data: members } = useSWR( workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null, diff --git a/web/components/issues/sidebar-select/cycle.tsx b/web/components/issues/sidebar-select/cycle.tsx index 86c9dcfca..5f5e6c386 100644 --- a/web/components/issues/sidebar-select/cycle.tsx +++ b/web/components/issues/sidebar-select/cycle.tsx @@ -14,6 +14,7 @@ import { CYCLE_ISSUES, INCOMPLETE_CYCLES_LIST, ISSUE_DETAILS } from "constants/f type Props = { issueDetail: IIssue | undefined; + projectId: string; handleCycleChange?: (cycleId: string) => void; disabled?: boolean; handleIssueUpdate?: () => void; @@ -26,7 +27,7 @@ export const SidebarCycleSelect: React.FC = (props) => { const { issueDetail, disabled = false, handleIssueUpdate, handleCycleChange } = props; // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, projectId: _projectId, peekProjectId } = router.query; // mobx store const { cycleIssues: { removeIssueFromCycle, addIssueToCycle }, @@ -34,6 +35,8 @@ export const SidebarCycleSelect: React.FC = (props) => { const [isUpdating, setIsUpdating] = useState(false); + const projectId = _projectId ?? peekProjectId; + const { data: incompleteCycles } = useSWR( workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null, workspaceSlug && projectId diff --git a/web/components/issues/sidebar-select/label.tsx b/web/components/issues/sidebar-select/label.tsx index b7ef3f48d..ca7abd8be 100644 --- a/web/components/issues/sidebar-select/label.tsx +++ b/web/components/issues/sidebar-select/label.tsx @@ -18,6 +18,7 @@ import { IIssue, IIssueLabel } from "types"; type Props = { issueDetails: IIssue | undefined; + projectId: string; labelList: string[]; submitChanges: (formData: any) => void; isNotAllowed: boolean; @@ -30,12 +31,12 @@ const defaultValues: Partial = { }; export const SidebarLabelSelect: React.FC = observer((props) => { - const { issueDetails, labelList, submitChanges, isNotAllowed, uneditable } = props; + const { issueDetails, projectId, labelList, submitChanges, isNotAllowed, uneditable } = props; // states const [createLabelForm, setCreateLabelForm] = useState(false); // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; // toast const { setToastAlert } = useToast(); // mobx store diff --git a/web/components/issues/sidebar-select/module.tsx b/web/components/issues/sidebar-select/module.tsx index c96799bc6..6b6db072b 100644 --- a/web/components/issues/sidebar-select/module.tsx +++ b/web/components/issues/sidebar-select/module.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -import { mutate } from "swr"; +import useSWR, { mutate } from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // ui @@ -9,28 +9,40 @@ import { CustomSearchSelect, DiceIcon, Spinner, Tooltip } from "@plane/ui"; // types import { IIssue } from "types"; // fetch-keys -import { ISSUE_DETAILS, MODULE_ISSUES } from "constants/fetch-keys"; +import { ISSUE_DETAILS, MODULE_ISSUES, MODULE_LIST } from "constants/fetch-keys"; +// services +import { ModuleService } from "services/module.service"; type Props = { issueDetail: IIssue | undefined; + projectId: string; handleModuleChange?: (moduleId: string) => void; disabled?: boolean; handleIssueUpdate?: () => void; }; +// services +const moduleService = new ModuleService(); + export const SidebarModuleSelect: React.FC = observer((props) => { - const { issueDetail, disabled = false, handleIssueUpdate, handleModuleChange } = props; + const { issueDetail, projectId, disabled = false, handleIssueUpdate, handleModuleChange } = props; // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug } = router.query; // mobx store const { - module: { projectModules }, moduleIssues: { removeIssueFromModule, addIssueToModule }, } = useMobxStore(); const [isUpdating, setIsUpdating] = useState(false); + const { data: projectModules } = useSWR( + workspaceSlug && projectId ? MODULE_LIST(projectId as string) : null, + workspaceSlug && projectId + ? () => moduleService.getModules(workspaceSlug as string, projectId as string) + : null + ); + const handleModuleStoreChange = async (moduleId: string) => { if (!workspaceSlug || !issueDetail || !moduleId) return; diff --git a/web/components/issues/sidebar-select/parent.tsx b/web/components/issues/sidebar-select/parent.tsx index cdeb09e90..d0a834190 100644 --- a/web/components/issues/sidebar-select/parent.tsx +++ b/web/components/issues/sidebar-select/parent.tsx @@ -12,15 +12,16 @@ import { IIssue, ISearchIssueResponse } from "types"; type Props = { onChange: (value: string) => void; issueDetails: IIssue | undefined; + projectId: string; disabled?: boolean; }; -export const SidebarParentSelect: React.FC = ({ onChange, issueDetails, disabled = false }) => { +export const SidebarParentSelect: React.FC = ({ onChange, issueDetails, projectId, disabled = false }) => { const [isParentModalOpen, setIsParentModalOpen] = useState(false); const [selectedParentIssue, setSelectedParentIssue] = useState(null); const router = useRouter(); - const { projectId, issueId } = router.query; + const { issueId } = router.query; return ( <> diff --git a/web/components/issues/sidebar-select/state.tsx b/web/components/issues/sidebar-select/state.tsx index 0fb3fe42f..decbf9459 100644 --- a/web/components/issues/sidebar-select/state.tsx +++ b/web/components/issues/sidebar-select/state.tsx @@ -15,6 +15,7 @@ import { STATES_LIST } from "constants/fetch-keys"; type Props = { value: string; + projectId: string; onChange: (val: string) => void; disabled?: boolean; }; @@ -22,9 +23,9 @@ type Props = { // services const stateService = new ProjectStateService(); -export const SidebarStateSelect: React.FC = ({ value, onChange, disabled = false }) => { +export const SidebarStateSelect: React.FC = ({ value, projectId, onChange, disabled = false }) => { const router = useRouter(); - const { workspaceSlug, projectId, inboxIssueId } = router.query; + const { workspaceSlug, inboxIssueId } = router.query; const { data: states } = useSWR( workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, diff --git a/web/components/issues/sidebar.tsx b/web/components/issues/sidebar.tsx index 4cb4d74a1..49c277ae2 100644 --- a/web/components/issues/sidebar.tsx +++ b/web/components/issues/sidebar.tsx @@ -290,6 +290,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => { render={({ field: { value } }) => ( submitChanges({ state: val })} disabled={!isAllowed || uneditable} /> @@ -311,6 +312,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => { render={({ field: { value } }) => ( submitChanges({ assignees: val })} disabled={!isAllowed || uneditable} /> @@ -382,6 +384,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => { onChange(val); }} issueDetails={issueDetail} + projectId={projectId as string} disabled={!isAllowed || uneditable} /> )} @@ -536,6 +539,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => {
@@ -551,6 +555,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => {
@@ -569,6 +574,7 @@ export const IssueDetailsSidebar: React.FC = observer((props) => {
Date: Mon, 18 Dec 2023 14:59:25 +0530 Subject: [PATCH 23/24] fix: bug fixes & improvements (#3159) * fix: on issue update updates_on not updating * fix: on project change cycle update * fix: update issue --- web/components/issues/select/cycle.tsx | 2 +- web/store/issues/global/issue.store.ts | 6 ++++++ web/store/issues/project-issues/cycle/issue.store.ts | 6 ++++++ web/store/issues/project-issues/draft/issue.store.ts | 6 ++++++ web/store/issues/project-issues/module/issue.store.ts | 6 ++++++ web/store/issues/project-issues/project-view/issue.store.ts | 6 ++++++ web/store/issues/project-issues/project/issue.store.ts | 6 ++++++ 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/web/components/issues/select/cycle.tsx b/web/components/issues/select/cycle.tsx index 2a9f4ee11..1c6921f26 100644 --- a/web/components/issues/select/cycle.tsx +++ b/web/components/issues/select/cycle.tsx @@ -34,7 +34,7 @@ export const IssueCycleSelect: React.FC = observer((props if (workspaceSlug && projectId) cycleStore.fetchCycles(workspaceSlug, projectId, "all"); }; - const cycles = cycleStore.projectCycles; + const cycles = cycleStore.cycles?.[projectId]?.["all"] ?? []; const selectedCycle = cycles ? cycles?.find((i) => i.id === value) : undefined; diff --git a/web/store/issues/global/issue.store.ts b/web/store/issues/global/issue.store.ts index 5610705b7..d4eb119af 100644 --- a/web/store/issues/global/issue.store.ts +++ b/web/store/issues/global/issue.store.ts @@ -182,6 +182,12 @@ export class GlobalIssuesStore extends IssueBaseStore implements IGlobalIssuesSt const response = await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[workspaceViewId][issueId] = { ..._issues[workspaceViewId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, workspaceViewId, "mutation"); diff --git a/web/store/issues/project-issues/cycle/issue.store.ts b/web/store/issues/project-issues/cycle/issue.store.ts index 767000902..b5c0088bd 100644 --- a/web/store/issues/project-issues/cycle/issue.store.ts +++ b/web/store/issues/project-issues/cycle/issue.store.ts @@ -239,6 +239,12 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor const response = await this.rootStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[cycleId][issueId] = { ..._issues[cycleId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId); diff --git a/web/store/issues/project-issues/draft/issue.store.ts b/web/store/issues/project-issues/draft/issue.store.ts index 31610dad2..90a6f63c7 100644 --- a/web/store/issues/project-issues/draft/issue.store.ts +++ b/web/store/issues/project-issues/draft/issue.store.ts @@ -158,6 +158,12 @@ export class ProjectDraftIssuesStore extends IssueBaseStore implements IProjectD const response = await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[projectId][issueId] = { ..._issues[projectId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/issues/project-issues/module/issue.store.ts b/web/store/issues/project-issues/module/issue.store.ts index 0f7fcae5b..b49ba6150 100644 --- a/web/store/issues/project-issues/module/issue.store.ts +++ b/web/store/issues/project-issues/module/issue.store.ts @@ -231,6 +231,12 @@ export class ModuleIssuesStore extends IssueBaseStore implements IModuleIssuesSt const response = await this.rootStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[moduleId][issueId] = { ..._issues[moduleId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation", moduleId); diff --git a/web/store/issues/project-issues/project-view/issue.store.ts b/web/store/issues/project-issues/project-view/issue.store.ts index c3562078a..7110c1dab 100644 --- a/web/store/issues/project-issues/project-view/issue.store.ts +++ b/web/store/issues/project-issues/project-view/issue.store.ts @@ -163,6 +163,12 @@ export class ViewIssuesStore extends IssueBaseStore implements IViewIssuesStore const response = await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[projectId][issueId] = { ..._issues[projectId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation"); diff --git a/web/store/issues/project-issues/project/issue.store.ts b/web/store/issues/project-issues/project/issue.store.ts index 8b8075179..25ec450ca 100644 --- a/web/store/issues/project-issues/project/issue.store.ts +++ b/web/store/issues/project-issues/project/issue.store.ts @@ -163,6 +163,12 @@ export class ProjectIssuesStore extends IssueBaseStore implements IProjectIssues const response = await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data); + runInAction(() => { + _issues = { ...this.issues }; + _issues[projectId][issueId] = { ..._issues[projectId][issueId], ...response }; + this.issues = _issues; + }); + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation"); From 81256d63732562ef1ad37b04f387b398e04a0ed0 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:17:38 +0530 Subject: [PATCH 24/24] chore: add tooltips to issue properties with no value (#3169) * chore: add tolltips to properties with no value * chore: update property types --- .../issue-layouts/kanban/properties.tsx | 8 +-- .../issues/issue-layouts/list/properties.tsx | 8 +-- .../issues/issue-layouts/properties/date.tsx | 63 ++++++++++--------- .../issue-layouts/properties/labels.tsx | 20 +++--- 4 files changed, 54 insertions(+), 45 deletions(-) diff --git a/web/components/issues/issue-layouts/kanban/properties.tsx b/web/components/issues/issue-layouts/kanban/properties.tsx index 788607690..9590c9068 100644 --- a/web/components/issues/issue-layouts/kanban/properties.tsx +++ b/web/components/issues/issue-layouts/kanban/properties.tsx @@ -57,7 +57,7 @@ export const KanBanProperties: React.FC = observer((props) => ); }; - const handleStartDate = (date: string) => { + const handleStartDate = (date: string | null) => { handleIssues( !sub_group_id && sub_group_id === "null" ? null : sub_group_id, !group_id && group_id === "null" ? null : group_id, @@ -65,7 +65,7 @@ export const KanBanProperties: React.FC = observer((props) => ); }; - const handleTargetDate = (date: string) => { + const handleTargetDate = (date: string | null) => { handleIssues( !sub_group_id && sub_group_id === "null" ? null : sub_group_id, !group_id && group_id === "null" ? null : group_id, @@ -122,7 +122,7 @@ export const KanBanProperties: React.FC = observer((props) => {displayProperties && displayProperties?.start_date && ( handleStartDate(date)} + onChange={(date) => handleStartDate(date)} disabled={isReadOnly} type="start_date" /> @@ -132,7 +132,7 @@ export const KanBanProperties: React.FC = observer((props) => {displayProperties && displayProperties?.due_date && ( handleTargetDate(date)} + onChange={(date) => handleTargetDate(date)} disabled={isReadOnly} type="target_date" /> diff --git a/web/components/issues/issue-layouts/list/properties.tsx b/web/components/issues/issue-layouts/list/properties.tsx index 8b6f54010..eeff3b273 100644 --- a/web/components/issues/issue-layouts/list/properties.tsx +++ b/web/components/issues/issue-layouts/list/properties.tsx @@ -40,11 +40,11 @@ export const ListProperties: FC = observer((props) => { handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, assignees: ids }); }; - const handleStartDate = (date: string) => { + const handleStartDate = (date: string | null) => { handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, start_date: date }); }; - const handleTargetDate = (date: string) => { + const handleTargetDate = (date: string | null) => { handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, target_date: date }); }; @@ -106,7 +106,7 @@ export const ListProperties: FC = observer((props) => { {displayProperties && displayProperties?.start_date && ( handleStartDate(date)} + onChange={(date) => handleStartDate(date)} disabled={isReadonly} type="start_date" /> @@ -116,7 +116,7 @@ export const ListProperties: FC = observer((props) => { {displayProperties && displayProperties?.due_date && ( handleTargetDate(date)} + onChange={(date) => handleTargetDate(date)} disabled={isReadonly} type="target_date" /> diff --git a/web/components/issues/issue-layouts/properties/date.tsx b/web/components/issues/issue-layouts/properties/date.tsx index 9077c68d8..cfe3481e3 100644 --- a/web/components/issues/issue-layouts/properties/date.tsx +++ b/web/components/issues/issue-layouts/properties/date.tsx @@ -12,11 +12,11 @@ import { Tooltip } from "@plane/ui"; // hooks import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderDateFormat, renderFormattedDate } from "helpers/date-time.helper"; export interface IIssuePropertyDate { - value: any; - onChange: (date: any) => void; + value: string | null; + onChange: (date: string | null) => void; disabled?: boolean; type: "start_date" | "target_date"; } @@ -57,33 +57,40 @@ export const IssuePropertyDate: React.FC = observer((props) <> e.stopPropagation()} > -
- - {value && ( - <> - -
{value}
-
- -
{ - if (onChange) onChange(null); - }} - > - -
- - )} -
+ +
+
+ + {value && ( + <> +
{value}
+
{ + if (onChange) onChange(null); + }} + > + +
+ + )} +
+
+
@@ -94,7 +101,7 @@ export const IssuePropertyDate: React.FC = observer((props) {({ close }) => ( { + onChange={(val, e) => { e?.stopPropagation(); if (onChange && val) { onChange(renderDateFormat(val)); diff --git a/web/components/issues/issue-layouts/properties/labels.tsx b/web/components/issues/issue-layouts/properties/labels.tsx index a4bd54338..282268d7b 100644 --- a/web/components/issues/issue-layouts/properties/labels.tsx +++ b/web/components/issues/issue-layouts/properties/labels.tsx @@ -107,7 +107,7 @@ export const IssuePropertyLabels: React.FC = observer((pro {projectLabels ?.filter((l) => value.includes(l.id)) .map((label) => ( - +
= observer((pro
) ) : ( -
- - {placeholderText} -
+ +
+ + {placeholderText} +
+
)}
);