From dadd2cf39b77e1d4299e935b34c168d9e56c27c5 Mon Sep 17 00:00:00 2001 From: Alexander Pushkov Date: Wed, 13 Dec 2023 19:04:12 +0300 Subject: [PATCH 01/11] 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 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 02/11] 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 07/11] 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 08/11] 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 09/11] 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 10/11] 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 11/11] 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} +
+
)}
);