diff --git a/package.json b/package.json index 97b793a5b..64bd22058 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ ], "scripts": { "build": "turbo run build", - "dev": "turbo run dev", + "dev": "turbo run dev --concurrency=13", "start": "turbo run start", "lint": "turbo run lint", "clean": "turbo run clean", @@ -34,4 +34,4 @@ "@types/react": "18.2.42" }, "packageManager": "yarn@1.22.19" -} +} \ No newline at end of file diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 9695eb931..44b91bb29 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -27,6 +27,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { onChange, options, onOpen, + onClose, optionsClassName = "", value, tabIndex, @@ -58,7 +59,10 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { setIsOpen(true); if (referenceElement) referenceElement.focus(); }; - const closeDropdown = () => setIsOpen(false); + const closeDropdown = () => { + setIsOpen(false); + onClose && onClose(); + }; const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); useOutsideClickDetector(dropdownRef, closeDropdown); diff --git a/packages/ui/src/dropdowns/helper.tsx b/packages/ui/src/dropdowns/helper.tsx index 453a33b63..06f1c44c0 100644 --- a/packages/ui/src/dropdowns/helper.tsx +++ b/packages/ui/src/dropdowns/helper.tsx @@ -36,6 +36,7 @@ export interface ICustomSelectProps extends IDropdownProps { interface CustomSearchSelectProps { footerOption?: JSX.Element; onChange: any; + onClose?: () => void; options: | { value: any; diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index b281b8c36..05bc2c6a4 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -26,7 +26,7 @@ import { capitalizeFirstLetter } from "helpers/string.helper"; // types import { IIssueActivity } from "@plane/types"; -const IssueLink = ({ activity }: { activity: IIssueActivity }) => { +export const IssueLink = ({ activity }: { activity: IIssueActivity }) => { const router = useRouter(); const { workspaceSlug } = router.query; @@ -341,7 +341,9 @@ const activityDetails: { if (activity.verb === "created") return ( <> - added this issue to the cycle + + added {showIssue ? : "this issue"} to the cycle{" "} + - added this issue to the module{" "} + added {showIssue ? : "this issue"} to the module{" "} - Issue. - + created )}

diff --git a/web/components/exporter/export-modal.tsx b/web/components/exporter/export-modal.tsx index 47555bf56..b1f529775 100644 --- a/web/components/exporter/export-modal.tsx +++ b/web/components/exporter/export-modal.tsx @@ -28,6 +28,7 @@ export const Exporter: React.FC = observer((props) => { const { isOpen, handleClose, user, provider, mutateServices } = props; // states const [exportLoading, setExportLoading] = useState(false); + const [isSelectOpen, setIsSelectOpen] = useState(false); // router const router = useRouter(); const { workspaceSlug } = router.query; @@ -91,7 +92,13 @@ export const Exporter: React.FC = observer((props) => { return ( - + { + if (!isSelectOpen) handleClose(); + }} + > = observer((props) => { .join(", ") : "All projects" } + onOpen={() => setIsSelectOpen(true)} + onClose={() => setIsSelectOpen(false)} optionsClassName="min-w-full" multiple /> diff --git a/web/components/issues/issue-layouts/filters/header/filters/state.tsx b/web/components/issues/issue-layouts/filters/header/filters/state.tsx index f473738d0..c13a69b0a 100644 --- a/web/components/issues/issue-layouts/filters/header/filters/state.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/state.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { observer } from "mobx-react"; // components import { FilterHeader, FilterOption } from "components/issues"; // ui @@ -13,7 +14,7 @@ type Props = { states: IState[] | undefined; }; -export const FilterState: React.FC = (props) => { +export const FilterState: React.FC = observer((props) => { const { appliedFilters, handleUpdate, searchQuery, states } = props; const [itemsToRender, setItemsToRender] = useState(5); @@ -75,4 +76,4 @@ export const FilterState: React.FC = (props) => { )} ); -}; +}); diff --git a/web/components/issues/issue-modal/form.tsx b/web/components/issues/issue-modal/form.tsx index 58a80151f..537ca0161 100644 --- a/web/components/issues/issue-modal/form.tsx +++ b/web/components/issues/issue-modal/form.tsx @@ -194,7 +194,7 @@ export const IssueFormRoot: FC = observer((props) => { const handleFormChange = () => { if (!onChange) return; - if (isDirty) onChange(watch()); + if (isDirty && (watch("name") || watch("description_html"))) onChange(watch()); else onChange(null); }; diff --git a/web/components/profile/overview/activity.tsx b/web/components/profile/overview/activity.tsx index 793527beb..58bbb6898 100644 --- a/web/components/profile/overview/activity.tsx +++ b/web/components/profile/overview/activity.tsx @@ -1,9 +1,12 @@ import { useRouter } from "next/router"; import useSWR from "swr"; +import { observer } from "mobx-react"; +//hooks +import { useUser } from "hooks/store"; // services import { UserService } from "services/user.service"; // components -import { ActivityMessage } from "components/core"; +import { ActivityMessage, IssueLink } from "components/core"; // ui import { ProfileEmptyState } from "components/ui"; import { Loader } from "@plane/ui"; @@ -17,9 +20,11 @@ import { USER_PROFILE_ACTIVITY } from "constants/fetch-keys"; // services const userService = new UserService(); -export const ProfileActivity = () => { +export const ProfileActivity = observer(() => { const router = useRouter(); const { workspaceSlug, userId } = router.query; + // store hooks + const { currentUser } = useUser(); const { data: userProfileActivity } = useSWR( workspaceSlug && userId ? USER_PROFILE_ACTIVITY(workspaceSlug.toString(), userId.toString()) : null, @@ -54,20 +59,14 @@ export const ProfileActivity = () => {

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

@@ -95,4 +94,4 @@ export const ProfileActivity = () => {
); -}; +}); diff --git a/web/components/workspace/sidebar-quick-action.tsx b/web/components/workspace/sidebar-quick-action.tsx index a2e813755..88fee55a6 100644 --- a/web/components/workspace/sidebar-quick-action.tsx +++ b/web/components/workspace/sidebar-quick-action.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useRef, useState } from "react"; import { observer } from "mobx-react-lite"; import { ChevronUp, PenSquare, Search } from "lucide-react"; // hooks @@ -25,10 +25,26 @@ export const WorkspaceSidebarQuickAction = observer(() => { const { storedValue, clearValue } = useLocalStorage("draftedIssue", JSON.stringify({})); + //useState control for displaying draft issue button instead of group hover + const [isDraftButtonOpen, setIsDraftButtonOpen] = useState(false); + + const timeoutRef = useRef(); + const isSidebarCollapsed = themeStore.sidebarCollapsed; const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; + const onMouseEnter = () => { + //if renet before timout clear the timeout + timeoutRef?.current && clearTimeout(timeoutRef.current); + setIsDraftButtonOpen(true); + }; + + const onMouseLeave = () => { + timeoutRef.current = setTimeout(() => { + setIsDraftButtonOpen(false); + }, 300); + }; return ( <> { className={`mt-4 flex w-full cursor-pointer items-center justify-between px-4 ${ isSidebarCollapsed ? "flex-col gap-1" : "gap-2" }`} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} > {isAuthorizedUser && (
{ isSidebarCollapsed ? "hidden" : "block" }`} > - +
diff --git a/web/pages/profile/activity.tsx b/web/pages/profile/activity.tsx index 0cd010905..65ccdcdc8 100644 --- a/web/pages/profile/activity.tsx +++ b/web/pages/profile/activity.tsx @@ -1,6 +1,9 @@ import { ReactElement } from "react"; import useSWR from "swr"; import Link from "next/link"; +import { observer } from "mobx-react"; +//hooks +import { useUser } from "hooks/store"; // services import { UserService } from "services/user.service"; // layouts @@ -21,8 +24,10 @@ import { NextPageWithLayout } from "lib/types"; const userService = new UserService(); -const ProfileActivityPage: NextPageWithLayout = () => { +const ProfileActivityPage: NextPageWithLayout = observer(() => { const { data: userActivity } = useSWR(USER_ACTIVITY, () => userService.getUserActivity()); + // store hooks + const { currentUser } = useUser(); return (
@@ -158,7 +163,9 @@ const ProfileActivityPage: NextPageWithLayout = () => { href={`/${activityItem.workspace_detail.slug}/profile/${activityItem.actor_detail.id}`} > - {activityItem.actor_detail.display_name} + {currentUser?.id === activityItem.actor_detail.id + ? "You" + : activityItem.actor_detail.display_name} )}{" "} @@ -189,7 +196,7 @@ const ProfileActivityPage: NextPageWithLayout = () => { )}
); -}; +}); ProfileActivityPage.getLayout = function getLayout(page: ReactElement) { return {page};