chore: run fixes (#8257)

* chore: run fixes

* fix: type, just use hocuspocusservercontext

* fix: codemod

---------

Co-authored-by: Palanikannan M <akashmalinimurugu@gmail.com>
This commit is contained in:
Aaron
2025-12-08 23:56:50 +07:00
committed by GitHub
parent a9e9cb2983
commit 0ab94ed6d6
172 changed files with 1784 additions and 1798 deletions

12
.npmrc
View File

@@ -45,15 +45,3 @@ prefer-frozen-lockfile = true
# Use isolated linker (best compatibility with Node ecosystem tools)
node-linker = isolated
# ------------------------------
# Hoisting Strategy
# ------------------------------
# Hoist commonly used tools to the root to prevent duplicates and speed up resolution
public-hoist-pattern[] = typescript
public-hoist-pattern[] = eslint
public-hoist-pattern[] = *@plane/*
public-hoist-pattern[] = vite
public-hoist-pattern[] = turbo

View File

@@ -11,7 +11,7 @@ import { cn } from "@plane/utils";
// hooks
import { useTheme } from "@/hooks/store";
// assets
// eslint-disable-next-line import/order
import packageJson from "package.json";
const helpOptions = [

View File

@@ -26,7 +26,7 @@ export enum EErrorAlertType {
}
const errorCodeMessages: {
[key in EAdminAuthErrorCodes]: { title: string; message: (email?: string | undefined) => React.ReactNode };
[key in EAdminAuthErrorCodes]: { title: string; message: (email?: string) => React.ReactNode };
} = {
// admin
[EAdminAuthErrorCodes.ADMIN_ALREADY_EXIST]: {
@@ -79,14 +79,11 @@ const errorCodeMessages: {
},
[EAdminAuthErrorCodes.ADMIN_USER_DEACTIVATED]: {
title: `User account deactivated`,
message: () => `User account deactivated. Please contact ${!!SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
message: () => `User account deactivated. Please contact ${SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
},
};
export const authErrorHandler = (
errorCode: EAdminAuthErrorCodes,
email?: string | undefined
): TAdminAuthErrorInfo | undefined => {
export const authErrorHandler = (errorCode: EAdminAuthErrorCodes, email?: string): TAdminAuthErrorInfo | undefined => {
const bannerAlertErrorCodes = [
EAdminAuthErrorCodes.ADMIN_ALREADY_EXIST,
EAdminAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME,

View File

@@ -1,4 +1,3 @@
/* eslint-disable import/order */
import * as Sentry from "@sentry/react-router";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";

View File

@@ -11,6 +11,7 @@ export function UpgradeButton() {
href="https://plane.so/pricing?mode=self-hosted"
target="_blank"
className={cn(getButtonStyling("primary", "sm"))}
rel="noreferrer"
>
Upgrade
<SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" />

View File

@@ -24,6 +24,7 @@ export const WorkspaceListItem = observer(function WorkspaceListItem({ workspace
href={`${WEB_BASE_URL}/${encodeURIComponent(workspace.slug)}`}
target="_blank"
className="group flex items-center justify-between p-4 gap-2.5 truncate border border-custom-border-200/70 hover:border-custom-border-200 hover:bg-custom-background-90 rounded-md"
rel="noreferrer"
>
<div className="flex items-start gap-4">
<span

View File

@@ -12,7 +12,7 @@ export class ForceCloseHandler implements Extension {
priority = 999;
async onConfigure({ instance }: onConfigurePayload) {
const redisExt = instance.configuration.extensions.find((ext) => ext instanceof Redis) as Redis | undefined;
const redisExt = instance.configuration.extensions.find((ext) => ext instanceof Redis);
if (!redisExt) {
logger.warn("[FORCE_CLOSE_HANDLER] Redis extension not found");
@@ -149,7 +149,7 @@ export const forceCloseDocumentAcrossServers = async (
logger.info(`[FORCE_CLOSE] Closed ${closedCount}/${connectionsBefore} local connections`);
// STEP 4: BROADCAST TO OTHER SERVERS
const redisExt = instance.configuration.extensions.find((ext) => ext instanceof Redis) as Redis | undefined;
const redisExt = instance.configuration.extensions.find((ext) => ext instanceof Redis);
if (redisExt) {
const commandData: ForceCloseCommandData = {

View File

@@ -1,4 +1,3 @@
// eslint-disable-next-line import/order
import { setupSentry } from "./instrument";
setupSentry();

View File

@@ -1,7 +1,7 @@
import type { Hocuspocus } from "@hocuspocus/server";
import { createRealtimeEvent } from "@plane/editor";
import { logger } from "@plane/logger";
import type { FetchPayloadWithContext, StorePayloadWithContext } from "@/types";
import type { HocusPocusServerContext } from "@/types";
import { broadcastMessageToPage } from "./broadcast-message";
// Helper to broadcast error to frontend
@@ -10,7 +10,7 @@ export const broadcastError = async (
pageId: string,
errorMessage: string,
errorType: "fetch" | "store",
context: FetchPayloadWithContext["context"] | StorePayloadWithContext["context"],
context: HocusPocusServerContext,
errorCode?: "content_too_large" | "page_locked" | "page_archived",
shouldDisconnect?: boolean
) => {

View File

@@ -1,4 +1,3 @@
/* eslint-disable import/order */
import * as Sentry from "@sentry/react-router";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";

View File

@@ -11,7 +11,7 @@ interface Props {
subGroupId: string | undefined,
isSubGroupCumulative: boolean
) => number | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
}
export const IssueLayoutHOC = observer(function IssueLayoutHOC(props: Props) {

View File

@@ -37,7 +37,7 @@ export interface IKanBan {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
showEmptyGroup?: boolean;
}

View File

@@ -29,7 +29,7 @@ interface IKanbanGroup {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
}

View File

@@ -37,7 +37,7 @@ export interface IKanBanSwimLanes {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
showEmptyGroup: boolean;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
orderBy: TIssueOrderByOptions | undefined;
@@ -163,7 +163,7 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
}
@@ -220,7 +220,7 @@ interface ISubGroup {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
loadMoreIssues: (groupId?: string, subGroupId?: string) => void;
}

View File

@@ -32,7 +32,7 @@ export interface IList {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
}
export const List = observer(function List(props: IList) {

View File

@@ -32,7 +32,7 @@ interface Props {
isSubGroupCumulative: boolean
) => number | undefined;
getPaginationData: (groupId: string | undefined, subGroupId: string | undefined) => TPaginationData | undefined;
getIssueLoader: (groupId?: string | undefined, subGroupId?: string | undefined) => TLoader;
getIssueLoader: (groupId?: string, subGroupId?: string) => TLoader;
}
// List loader component

View File

@@ -1,5 +1,3 @@
"use client";
import { observer } from "mobx-react";
// plane ui
import { StateGroupIcon } from "@plane/propel/icons";
@@ -24,7 +22,7 @@ type Props = {
}
);
export const IssueBlockState: React.FC<Props> = observer((props) => {
export const IssueBlockState = observer(function IssueBlockState(props: Props) {
const { shouldShowBorder = true } = props;
// store hooks
const { getStateById } = useStates();

View File

@@ -44,7 +44,7 @@ export const getGroupByColumns = (
case "labels":
return getLabelsColumns(label) as any;
case "assignees":
return getAssigneeColumns(member) as any;
return getAssigneeColumns(member);
case "created_by":
return getCreatedByColumns(member) as any;
default:
@@ -65,7 +65,7 @@ const getCycleColumns = (cycleStore: ICycleStore): IGroupByColumn[] | undefined
cycleGroups.push({
id: cycle.id,
name: cycle.name,
icon: <CycleGroupIcon cycleGroup={cycleStatus as TCycleGroups} className="h-3.5 w-3.5" />,
icon: <CycleGroupIcon cycleGroup={cycleStatus} className="h-3.5 w-3.5" />,
payload: { cycle_id: cycle.id },
});
}
@@ -196,11 +196,8 @@ export const getDisplayPropertiesCount = (
return count;
};
export const getIssueBlockId = (
issueId: string | undefined,
groupId: string | undefined,
subGroupId?: string | undefined
) => `issue_${issueId}_${groupId}_${subGroupId}`;
export const getIssueBlockId = (issueId: string | undefined, groupId: string | undefined, subGroupId?: string) =>
`issue_${issueId}_${groupId}_${subGroupId}`;
/**
* returns empty Array if groupId is None

View File

@@ -6,7 +6,7 @@ const useClipboardWritePermission = () => {
useEffect(() => {
const checkClipboardWriteAccess = () => {
navigator.permissions
//eslint-disable-next-line no-undef
.query({ name: "clipboard-write" as PermissionName })
.then((result) => {
if (result.state === "granted") {

View File

@@ -32,7 +32,6 @@ export const useIntersectionObserver = (
observer.observe(elementRef);
return () => {
if (elementRef) {
// eslint-disable-next-line react-hooks/exhaustive-deps
observer.unobserve(elementRef);
}
};

View File

@@ -103,15 +103,15 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
const allIssues = groupedIssueIds[ALL_ISSUES] ?? [];
if (allIssues && Array.isArray(allIssues)) {
return allIssues as string[];
return allIssues;
}
if (groupId && groupedIssueIds?.[groupId] && Array.isArray(groupedIssueIds[groupId])) {
return (groupedIssueIds[groupId] ?? []) as string[];
return groupedIssueIds[groupId] ?? [];
}
if (groupId && subGroupId) {
return ((groupedIssueIds as TSubGroupedIssues)[groupId]?.[subGroupId] ?? []) as string[];
return (groupedIssueIds as TSubGroupedIssues)[groupId]?.[subGroupId] ?? [];
}
return undefined;
@@ -443,7 +443,7 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
// if groupedIssueIds is an array, update the `groupedIssueIds` store at the issuePath
if (groupedIssueIds && Array.isArray(groupedIssueIds)) {
update(this, ["groupedIssueIds", ...issuePath], (issueIds: string[] = []) =>
uniq(concat(issueIds, groupedIssueIds as string[]))
uniq(concat(issueIds, groupedIssueIds))
);
// return true to indicate the store has been updated
return true;

View File

@@ -86,7 +86,7 @@ export type TAuthErrorInfo = {
};
const errorCodeMessages: {
[key in EAuthenticationErrorCodes]: { title: string; message: (email?: string | undefined) => React.ReactNode };
[key in EAuthenticationErrorCodes]: { title: string; message: (email?: string) => React.ReactNode };
} = {
// global
[EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED]: {
@@ -156,7 +156,7 @@ const errorCodeMessages: {
// sign in
[EAuthenticationErrorCodes.USER_ACCOUNT_DEACTIVATED]: {
title: `User account deactivated`,
message: () => `User account deactivated. Please contact ${!!SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
message: () => `User account deactivated. Please contact ${SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`,
},
[EAuthenticationErrorCodes.USER_DOES_NOT_EXIST]: {
@@ -332,10 +332,7 @@ const errorCodeMessages: {
},
};
export const authErrorHandler = (
errorCode: EAuthenticationErrorCodes,
email?: string | undefined
): TAuthErrorInfo | undefined => {
export const authErrorHandler = (errorCode: EAuthenticationErrorCodes, email?: string): TAuthErrorInfo | undefined => {
const bannerAlertErrorCodes = [
EAuthenticationErrorCodes.INSTANCE_NOT_CONFIGURED,
EAuthenticationErrorCodes.INVALID_EMAIL,

View File

@@ -1,5 +1,3 @@
"use client";
import { useState, useEffect } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/navigation";

View File

@@ -1,5 +1,3 @@
"use client";
import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
@@ -16,7 +14,7 @@ import { useAppRouter } from "@/hooks/use-app-router";
// plane web imports
import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common";
export const WorkItemDetailsHeader = observer(() => {
export const WorkItemDetailsHeader = observer(function WorkItemDetailsHeader() {
// router
const router = useAppRouter();
const { workspaceSlug, workItem } = useParams();

View File

@@ -50,11 +50,7 @@ export const CycleIssuesHeader = observer(function CycleIssuesHeader() {
const [analyticsModal, setAnalyticsModal] = useState(false);
// router
const router = useAppRouter();
const { workspaceSlug, projectId, cycleId } = useParams() as {
workspaceSlug: string;
projectId: string;
cycleId: string;
};
const { workspaceSlug, projectId, cycleId } = useParams();
// i18n
const { t } = useTranslation();
// store hooks

View File

@@ -22,10 +22,7 @@ export const ProjectIssuesMobileHeader = observer(function ProjectIssuesMobileHe
// i18n
const { t } = useTranslation();
const [analyticsModal, setAnalyticsModal] = useState(false);
const { workspaceSlug, projectId } = useParams() as {
workspaceSlug: string;
projectId: string;
};
const { workspaceSlug, projectId } = useParams();
const { currentProjectDetails } = useProject();
// store hooks

View File

@@ -1,5 +1,3 @@
"use client";
import { observer } from "mobx-react";
import { Outlet } from "react-router";
// plane imports

View File

@@ -25,11 +25,7 @@ const SUPPORTED_LAYOUTS = [
export const ModuleIssuesMobileHeader = observer(function ModuleIssuesMobileHeader() {
// router
const { workspaceSlug, projectId, moduleId } = useParams() as {
workspaceSlug: string;
projectId: string;
moduleId: string;
};
const { workspaceSlug, projectId, moduleId } = useParams();
// states
const [analyticsModal, setAnalyticsModal] = useState(false);
// plane hooks

View File

@@ -21,7 +21,7 @@ import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/com
export const ModulesListHeader = observer(function ModulesListHeader() {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
const { workspaceSlug, projectId } = useParams();
// store hooks
const { toggleCreateModuleModal } = useCommandPalette();
const { allowPermissions } = useUserPermissions();

View File

@@ -15,7 +15,7 @@ import { useProject } from "@/hooks/store/use-project";
import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common";
export const ProjectViewsHeader = observer(function ProjectViewsHeader() {
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
const { workspaceSlug, projectId } = useParams();
// store hooks
const { toggleCreateViewModal } = useCommandPalette();
const { loader } = useProject();

View File

@@ -1,4 +1,3 @@
/* eslint-disable import/order */
import * as Sentry from "@sentry/react-router";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";

View File

@@ -1,6 +1,4 @@
// hoc/withDockItems.tsx
"use client";
import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
@@ -13,7 +11,7 @@ type WithDockItemsProps = {
};
export function withDockItems<P extends WithDockItemsProps>(WrappedComponent: React.ComponentType<P>) {
const ComponentWithDockItems = observer((props: Omit<P, keyof WithDockItemsProps>) => {
const ComponentWithDockItems = observer(function ComponentWithDockItems(props: Omit<P, keyof WithDockItemsProps>) {
const { workspaceSlug } = useParams();
const { isProjectsPath, isNotificationsPath } = useWorkspacePaths();

View File

@@ -35,7 +35,7 @@ import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/com
export const IssuesHeader = observer(function IssuesHeader() {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
issues: { getGroupIssueCount },

View File

@@ -69,7 +69,7 @@ export const PaidPlanUpgradeModal = observer(function PaidPlanUpgradeModal(props
verticalFeatureList
extraFeatures={
<p className={COMMON_EXTRA_FEATURES_CLASSNAME}>
<a href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.PRO]} target="_blank">
<a href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.PRO]} target="_blank" rel="noreferrer">
See full features list
</a>
</p>
@@ -87,7 +87,11 @@ export const PaidPlanUpgradeModal = observer(function PaidPlanUpgradeModal(props
verticalFeatureList
extraFeatures={
<p className={COMMON_EXTRA_FEATURES_CLASSNAME}>
<a href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.BUSINESS]} target="_blank">
<a
href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.BUSINESS]}
target="_blank"
rel="noreferrer"
>
See full features list
</a>
</p>
@@ -105,7 +109,11 @@ export const PaidPlanUpgradeModal = observer(function PaidPlanUpgradeModal(props
verticalFeatureList
extraFeatures={
<p className={COMMON_EXTRA_FEATURES_CLASSNAME}>
<a href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.ENTERPRISE]} target="_blank">
<a
href={SUBSCRIPTION_WEBPAGE_URLS[EProductSubscriptionEnum.ENTERPRISE]}
target="_blank"
rel="noreferrer"
>
See full features list
</a>
</p>

View File

@@ -13,7 +13,7 @@ import { InboxIcon } from "@plane/propel/icons";
import useSWR from "swr";
import { useWorkspaceNotifications } from "@/hooks/store/notifications";
export const TopNavigationRoot = observer(() => {
export const TopNavigationRoot = observer(function TopNavigationRoot() {
// router
const { workspaceSlug, projectId, workItem } = useParams();
const pathname = usePathname();

View File

@@ -65,7 +65,7 @@ export const useProjectColumns = (props: TUseProjectColumnsProps) => {
tdRender: (rowData: RowData) => (
<NameColumn
rowData={rowData}
workspaceSlug={workspaceSlug as string}
workspaceSlug={workspaceSlug}
isAdmin={isAdmin}
currentUser={currentUser}
setRemoveMemberModal={setRemoveMemberModal}
@@ -110,8 +110,8 @@ export const useProjectColumns = (props: TUseProjectColumnsProps) => {
<AccountTypeColumn
rowData={rowData}
currentProjectRole={currentProjectRole}
projectId={projectId as string}
workspaceSlug={workspaceSlug as string}
projectId={projectId}
workspaceSlug={workspaceSlug}
/>
),
},

View File

@@ -1,5 +1,3 @@
"use client";
import React from "react";
import { observer } from "mobx-react";
import { AppRailVisibilityProvider as CoreProvider } from "@/lib/app-rail";
@@ -12,6 +10,8 @@ interface AppRailVisibilityProviderProps {
* CE AppRailVisibilityProvider
* Wraps core provider with isEnabled hardcoded to false
*/
export const AppRailVisibilityProvider = observer(({ children }: AppRailVisibilityProviderProps) => (
<CoreProvider isEnabled={false}>{children}</CoreProvider>
));
export const AppRailVisibilityProvider = observer(function AppRailVisibilityProvider({
children,
}: AppRailVisibilityProviderProps) {
return <CoreProvider isEnabled={false}>{children}</CoreProvider>;
});

View File

@@ -17,10 +17,10 @@ export const getTimelineStore = (
return timelineStore.modulesTimeLineStore as IBaseTimelineStore;
}
if (timelineType === GANTT_TIMELINE_TYPE.PROJECT) {
return timelineStore.projectTimeLineStore as IBaseTimelineStore;
return timelineStore.projectTimeLineStore;
}
if (timelineType === GANTT_TIMELINE_TYPE.GROUPED) {
return timelineStore.groupedTimeLineStore as IBaseTimelineStore;
return timelineStore.groupedTimeLineStore;
}
throw new Error(`Unknown timeline type: ${timelineType}`);
};

View File

@@ -49,7 +49,7 @@ export function GeneratedTokenDetails(props: Props) {
<div className="mt-6 flex items-center justify-between">
<p className="text-xs text-custom-text-400">
{tokenDetails.expired_at
? `Expires ${renderFormattedDate(tokenDetails.expired_at!)} at ${renderFormattedTime(tokenDetails.expired_at!)}`
? `Expires ${renderFormattedDate(tokenDetails.expired_at)} at ${renderFormattedTime(tokenDetails.expired_at)}`
: "Never expires"}
</p>
<Button variant="neutral-primary" size="sm" onClick={handleClose}>

View File

@@ -51,7 +51,7 @@ export function ApiTokenListItem(props: Props) {
<p className="mb-1 text-xs leading-6 text-custom-text-400">
{token.is_active
? token.expired_at
? `Expires ${renderFormattedDate(token.expired_at!)} at ${renderFormattedTime(token.expired_at!)}`
? `Expires ${renderFormattedDate(token.expired_at)} at ${renderFormattedTime(token.expired_at)}`
: "Never expires"
: `Expired ${calculateTimeAgo(token.expired_at)}`}
</p>

View File

@@ -42,7 +42,7 @@ export const CommentCreate = observer(function CommentCreate(props: TCommentCrea
// store hooks
const workspaceStore = useWorkspace();
// derived values
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug)?.id as string;
// form info
const {
handleSubmit,

View File

@@ -65,7 +65,7 @@ export const CommentsWrapper = observer(function CommentsWrapper(props: TComment
<CommentCard
key={comment.id}
workspaceSlug={workspaceSlug}
comment={comment as TIssueComment}
comment={comment}
activityOperations={activityOperations}
disabled={!isEditingAllowed}
ends={index === 0 ? "top" : index === comments.length - 1 ? "bottom" : undefined}

View File

@@ -1,4 +1,3 @@
import type { FC } from "react";
import { useMemo } from "react";
import { observer } from "mobx-react";
import { Globe2, Link, Lock, Pencil, Trash2 } from "lucide-react";
@@ -31,46 +30,48 @@ export const CommentQuickActions = observer(function CommentQuickActions(props:
// translation
const { t } = useTranslation();
const MENU_ITEMS = useMemo<TContextMenuItem[]>(
() => [
{
key: "edit",
action: setEditMode,
title: t("common.actions.edit"),
icon: Pencil,
shouldRender: canEdit,
},
{
key: "copy_link",
action: () => activityOperations.copyCommentLink(comment.id),
title: t("common.actions.copy_link"),
icon: Link,
shouldRender: showCopyLinkOption,
},
{
key: "access_specifier",
action: () =>
activityOperations.updateComment(comment.id, {
access:
comment.access === EIssueCommentAccessSpecifier.INTERNAL
? EIssueCommentAccessSpecifier.EXTERNAL
: EIssueCommentAccessSpecifier.INTERNAL,
}),
title:
comment.access === EIssueCommentAccessSpecifier.INTERNAL
? t("issue.comments.switch.public")
: t("issue.comments.switch.private"),
icon: comment.access === EIssueCommentAccessSpecifier.INTERNAL ? Globe2 : Lock,
shouldRender: showAccessSpecifier,
},
{
key: "delete",
action: () => activityOperations.removeComment(comment.id),
title: t("common.actions.delete"),
icon: Trash2,
shouldRender: canDelete,
},
],
const MENU_ITEMS = useMemo(
function MENU_ITEMS(): TContextMenuItem[] {
return [
{
key: "edit",
action: setEditMode,
title: t("common.actions.edit"),
icon: Pencil,
shouldRender: canEdit,
},
{
key: "copy_link",
action: () => activityOperations.copyCommentLink(comment.id),
title: t("common.actions.copy_link"),
icon: Link,
shouldRender: showCopyLinkOption,
},
{
key: "access_specifier",
action: () =>
activityOperations.updateComment(comment.id, {
access:
comment.access === EIssueCommentAccessSpecifier.INTERNAL
? EIssueCommentAccessSpecifier.EXTERNAL
: EIssueCommentAccessSpecifier.INTERNAL,
}),
title:
comment.access === EIssueCommentAccessSpecifier.INTERNAL
? t("issue.comments.switch.public")
: t("issue.comments.switch.private"),
icon: comment.access === EIssueCommentAccessSpecifier.INTERNAL ? Globe2 : Lock,
shouldRender: showAccessSpecifier,
},
{
key: "delete",
action: () => activityOperations.removeComment(comment.id),
title: t("common.actions.delete"),
icon: Trash2,
shouldRender: canDelete,
},
];
},
[t, setEditMode, canEdit, showCopyLinkOption, activityOperations, comment, showAccessSpecifier, canDelete]
);

View File

@@ -1,4 +1,3 @@
import type { ReactNode, FC } from "react";
import React, { useMemo } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
@@ -19,7 +18,7 @@ const IconWrapper = React.memo(function IconWrapper({ icon }: { icon: React.Reac
IconWrapper.displayName = "IconWrapper";
const LabelWrapper = React.memo(function LabelWrapper({ label }: { label: ReactNode }) {
const LabelWrapper = React.memo(function LabelWrapper({ label }: { label: React.ReactNode }) {
return <div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>;
});
@@ -30,7 +29,7 @@ const BreadcrumbContent = React.memo(function BreadcrumbContent({
label,
}: {
icon?: React.ReactNode;
label?: ReactNode;
label?: React.ReactNode;
}) {
if (!icon && !label) return null;
@@ -58,10 +57,10 @@ export const BreadcrumbLink = observer(function BreadcrumbLink(props: Props) {
const { isMobile } = usePlatformOS();
const itemWrapperProps = useMemo(
() => ({
(): Omit<React.ComponentProps<typeof ItemWrapper>, "children"> => ({
label: label?.toString(),
disableTooltip: isMobile || disableTooltip,
type: (href && href !== "" ? "link" : "text") as "link" | "text",
type: href && href !== "" ? "link" : "text",
isLast,
}),
[href, label, isMobile, disableTooltip, isLast]

View File

@@ -105,7 +105,7 @@ export const BulkDeleteIssuesModal = observer(function BulkDeleteIssuesModal(pro
if (!Array.isArray(data.delete_issue_ids)) data.delete_issue_ids = [data.delete_issue_ids];
await removeBulkIssues(workspaceSlug as string, projectId as string, data.delete_issue_ids)
await removeBulkIssues(workspaceSlug, projectId, data.delete_issue_ids)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,

View File

@@ -1,5 +1,3 @@
"use client";
import React, { useState } from "react";
import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form";
@@ -29,7 +27,7 @@ const defaultValues: TUniqueCodeValuesForm = { email: "", code: "" };
// service initialization
const authService = new AuthService();
export const ChangeEmailModal: React.FC<Props> = observer((props) => {
export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props) {
const { isOpen, onClose } = props;
// states
const [currentStep, setCurrentStep] = useState<TModalStep>("EMAIL");

View File

@@ -106,7 +106,7 @@ export const CycleSidebarHeader = observer(function CycleSidebarHeader(props: Pr
const dateChecker = async (payload: any) => {
try {
const res = await cycleService.cycleDateCheck(workspaceSlug as string, projectId as string, payload);
const res = await cycleService.cycleDateCheck(workspaceSlug, projectId, payload);
return res.status;
} catch (err) {
return false;

View File

@@ -1,5 +1,3 @@
"use client";
import type { ReactNode } from "react";
import { useRef, useState } from "react";
import { observer } from "mobx-react";
@@ -41,7 +39,9 @@ export type TWorkItemStateDropdownBaseProps = TDropdownProps & {
value: string | undefined | null;
};
export const WorkItemStateDropdownBase: React.FC<TWorkItemStateDropdownBaseProps> = observer((props) => {
export const WorkItemStateDropdownBase = observer(function WorkItemStateDropdownBase(
props: TWorkItemStateDropdownBaseProps
) {
const {
button,
buttonClassName,
@@ -79,7 +79,7 @@ export const WorkItemStateDropdownBase: React.FC<TWorkItemStateDropdownBaseProps
const { t } = useTranslation();
const statesList = stateIds.map((stateId) => getStateById(stateId)).filter((state) => !!state);
const defaultState = statesList?.find((state) => state?.default) || statesList[0];
const stateValue = !!value ? value : showDefaultState ? defaultState?.id : undefined;
const stateValue = value ? value : showDefaultState ? defaultState?.id : undefined;
// popper-js init
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",

View File

@@ -1,5 +1,3 @@
"use client";
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
@@ -16,7 +14,7 @@ type TWorkItemStateDropdownProps = Omit<
stateIds?: string[];
};
export const IntakeStateDropdown: React.FC<TWorkItemStateDropdownProps> = observer((props) => {
export const IntakeStateDropdown = observer(function IntakeStateDropdown(props: TWorkItemStateDropdownProps) {
const { projectId, stateIds: propsStateIds } = props;
// router params
const { workspaceSlug } = useParams();

View File

@@ -79,7 +79,7 @@ export const WorkItemStateDropdownBase = observer(function WorkItemStateDropdown
const { t } = useTranslation();
const statesList = stateIds.map((stateId) => getStateById(stateId)).filter((state) => !!state);
const defaultState = statesList?.find((state) => state?.default);
const stateValue = !!value ? value : showDefaultState ? defaultState?.id : undefined;
const stateValue = value ? value : showDefaultState ? defaultState?.id : undefined;
// popper-js init
const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: placement ?? "bottom-start",

View File

@@ -82,7 +82,7 @@ export const EstimatePointCreate = observer(function EstimatePointCreate(props:
const currentEstimatePointValues = estimatePoints
.map((point) => point?.value || undefined)
.filter((value) => value != undefined) as string[];
.filter((value) => value != undefined);
const isRepeated =
(estimateType && isEstimatePointValuesRepeated(currentEstimatePointValues, estimateType, estimateInputValue)) ||
false;

View File

@@ -87,7 +87,7 @@ export const EstimatePointUpdate = observer(function EstimatePointUpdate(props:
const currentEstimatePointValues = estimatePoints
.map((point) => (point?.key != estimatePoint?.key ? point?.value : undefined))
.filter((value) => value != undefined) as string[];
.filter((value) => value != undefined);
const isRepeated =
(estimateType && isEstimatePointValuesRepeated(currentEstimatePointValues, estimateType, estimateInputValue)) ||
false;

View File

@@ -104,6 +104,7 @@ export const EstimateRoot = observer(function EstimateRoot(props: TEstimateRoot)
href={"https://docs.plane.so/core-concepts/projects/run-project#estimate"}
target="_blank"
className="text-custom-primary-100/80 hover:text-custom-primary-100"
rel="noreferrer"
>
here.
</a>

View File

@@ -102,7 +102,7 @@ export const ExportForm = observer(function ExportForm(props: Props) {
rich_filters: formData.filters,
};
await projectExportService
.csvExport(workspaceSlug as string, payload)
.csvExport(workspaceSlug, payload)
.then(() => {
mutateServices();
setExportLoading(false);

View File

@@ -74,7 +74,7 @@ export const Exporter = observer(function Exporter(props: Props) {
multiple: multiple,
};
await projectExportService
.csvExport(workspaceSlug as string, payload)
.csvExport(workspaceSlug, payload)
.then(() => {
mutateServices();
router.push(`/${workspaceSlug}/settings/exports`);

View File

@@ -20,16 +20,11 @@ const IntegrationGuide = observer(function IntegrationGuide() {
<div className="h-full w-full">
<>
<ExportForm
workspaceSlug={workspaceSlug as string}
workspaceSlug={workspaceSlug}
provider={provider}
mutateServices={() => mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`))}
/>
<PrevExports
workspaceSlug={workspaceSlug as string}
cursor={cursor}
per_page={per_page}
setCursor={setCursor}
mutateServices={() => mutate(EXPORT_SERVICES_LIST(workspaceSlug, `${cursor}`, `${per_page}`))}
/>
<PrevExports workspaceSlug={workspaceSlug} cursor={cursor} per_page={per_page} setCursor={setCursor} />
</>
</div>
</>

View File

@@ -35,15 +35,13 @@ export const PrevExports = observer(function PrevExports(props: Props) {
const columns = useExportColumns();
const { data: exporterServices } = useSWR(
workspaceSlug && cursor ? EXPORT_SERVICES_LIST(workspaceSlug as string, cursor, `${per_page}`) : null,
workspaceSlug && cursor
? () => integrationService.getExportsServicesList(workspaceSlug as string, cursor, per_page)
: null
workspaceSlug && cursor ? EXPORT_SERVICES_LIST(workspaceSlug, cursor, `${per_page}`) : null,
workspaceSlug && cursor ? () => integrationService.getExportsServicesList(workspaceSlug, cursor, per_page) : null
);
const handleRefresh = () => {
setRefreshing(true);
mutate(EXPORT_SERVICES_LIST(workspaceSlug as string, `${cursor}`, `${per_page}`)).then(() => setRefreshing(false));
mutate(EXPORT_SERVICES_LIST(workspaceSlug, `${cursor}`, `${per_page}`)).then(() => setRefreshing(false));
};
useEffect(() => {

View File

@@ -14,7 +14,7 @@ type Props = {
export function SingleExport({ service, refreshing }: Props) {
const provider = service.provider;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [isLoading] = useState(false);
const checkExpiry = (inputDateString: string) => {

View File

@@ -59,7 +59,7 @@ export const timePreview = (date: Date) => {
export const datePreview = (date: Date, includeTime: boolean = false) => {
const day = date.getDate();
let month: number | WeekMonthDataType = date.getMonth();
month = months[month as number] as WeekMonthDataType;
month = months[month];
const year = date.getFullYear();
return `${charCapitalize(month?.shortTitle)} ${day}, ${year}${includeTime ? `, ${timePreview(date)}` : ``}`;

View File

@@ -5,7 +5,7 @@ export const handleOrderChange = (
droppedBlockId: string | undefined,
dropAtEndOfList: boolean,
blockIds: string[] | null,
getBlockById: (id: string, currentViewData?: ChartDataType | undefined) => IGanttBlock,
getBlockById: (id: string, currentViewData?: ChartDataType) => IGanttBlock,
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void
) => {
if (!blockIds || !draggingBlockId || !droppedBlockId) return;

View File

@@ -15,6 +15,7 @@ export function ProductUpdatesFooter() {
href="https://go.plane.so/p-docs"
target="_blank"
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
rel="noreferrer"
>
{t("docs")}
</a>
@@ -26,6 +27,7 @@ export function ProductUpdatesFooter() {
href="https://go.plane.so/p-changelog"
target="_blank"
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
rel="noreferrer"
>
{t("full_changelog")}
</a>
@@ -36,6 +38,7 @@ export function ProductUpdatesFooter() {
href="mailto:support@plane.so"
target="_blank"
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
rel="noreferrer"
>
{t("support")}
</a>
@@ -46,6 +49,7 @@ export function ProductUpdatesFooter() {
href="https://go.plane.so/p-discord"
target="_blank"
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
rel="noreferrer"
>
Discord
</a>
@@ -57,6 +61,7 @@ export function ProductUpdatesFooter() {
getButtonStyling("accent-primary", "sm"),
"flex gap-1.5 items-center text-center font-medium hover:underline underline-offset-2 outline-none"
)}
rel="noreferrer"
>
<PlaneLogo className="h-4 w-auto text-custom-text-100" />
{t("powered_by_plane_pages")}

View File

@@ -46,7 +46,7 @@ export const WidgetItem = observer(function WidgetItem(props: Props) {
const { widgetsMap } = useHome();
const { t } = useTranslation();
// derived values
const widget = widgetsMap[widgetId] as TWidgetEntityData;
const widget = widgetsMap[widgetId];
const widgetTitle = HOME_WIDGETS_LIST[widget.key]?.title;
// drag and drop

View File

@@ -90,11 +90,10 @@ export const InboxIssueMainContent = observer(function InboxIssueMainContent(pro
const issueOperations: TIssueOperations = useMemo(
() => ({
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, arrow-body-style
fetch: async (_workspaceSlug: string, _projectId: string, _issueId: string) => {
return;
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, arrow-body-style
remove: async (_workspaceSlug: string, _projectId: string, _issueId: string) => {
try {
await removeIssue(workspaceSlug, projectId, _issueId);

View File

@@ -43,8 +43,8 @@ export const DeleteInboxIssueModal = observer(function DeleteInboxIssueModal({
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: `${t("success")!}`,
message: `${t("inbox_issue.modals.delete.success")!}`,
title: `${t("success")}`,
message: `${t("inbox_issue.modals.delete.success")}`,
});
})
.catch((errors) => {

View File

@@ -41,13 +41,13 @@ export function DeleteImportModal({ isOpen, handleClose, data }: Props) {
setDeleteLoading(true);
mutate<IImporterService[]>(
IMPORTER_SERVICES_LIST(workspaceSlug as string),
IMPORTER_SERVICES_LIST(workspaceSlug),
(prevData) => (prevData ?? []).filter((i) => i.id !== data.id),
false
);
integrationService
.deleteImporterService(workspaceSlug as string, data.service, data.id)
.deleteImporterService(workspaceSlug, data.service, data.id)
.catch(() =>
setToast({
type: TOAST_TYPE.ERROR,

View File

@@ -30,10 +30,10 @@ export function GithubRepoDetails({ selectedRepo, handleStepChange, setUsers, se
const { workspaceSlug } = useParams();
const { data: repoInfo } = useSWR(
workspaceSlug && selectedRepo ? GITHUB_REPOSITORY_INFO(workspaceSlug as string, selectedRepo.name) : null,
workspaceSlug && selectedRepo ? GITHUB_REPOSITORY_INFO(workspaceSlug, selectedRepo.name) : null,
workspaceSlug && selectedRepo
? () =>
githubIntegrationService.getGithubRepoInfo(workspaceSlug as string, {
githubIntegrationService.getGithubRepoInfo(workspaceSlug, {
owner: selectedRepo.owner.login,
repo: selectedRepo.name,
})

View File

@@ -96,8 +96,8 @@ export function GithubImporterRoot() {
const { data: appIntegrations } = useSWR(APP_INTEGRATIONS, () => integrationService.getAppIntegrationsList());
const { data: workspaceIntegrations } = useSWR(
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
workspaceSlug ? () => integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug) : null,
workspaceSlug ? () => integrationService.getWorkspaceIntegrationsList(workspaceSlug) : null
);
const activeIntegrationState = () => {
@@ -138,10 +138,10 @@ export function GithubImporterRoot() {
};
await githubIntegrationService
.createGithubServiceImport(workspaceSlug as string, payload)
.createGithubServiceImport(workspaceSlug, payload)
.then(() => {
router.push(`/${workspaceSlug}/settings/imports`);
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string));
mutate(IMPORTER_SERVICES_LIST(workspaceSlug));
})
.catch(() =>
setToast({

View File

@@ -54,8 +54,8 @@ const IntegrationGuide = observer(function IntegrationGuide() {
const { t } = useTranslation();
const { data: importerServices } = useSWR(
workspaceSlug ? IMPORTER_SERVICES_LIST(workspaceSlug as string) : null,
workspaceSlug ? () => integrationService.getImporterServicesList(workspaceSlug as string) : null
workspaceSlug ? IMPORTER_SERVICES_LIST(workspaceSlug) : null,
workspaceSlug ? () => integrationService.getImporterServicesList(workspaceSlug) : null
);
const handleDeleteImport = (importService: IImporterService) => {
@@ -129,7 +129,7 @@ const IntegrationGuide = observer(function IntegrationGuide() {
className="flex flex-shrink-0 items-center gap-1 rounded bg-custom-background-80 px-1.5 py-1 text-xs outline-none"
onClick={() => {
setRefreshing(true);
mutate(IMPORTER_SERVICES_LIST(workspaceSlug as string)).then(() => setRefreshing(false));
mutate(IMPORTER_SERVICES_LIST(workspaceSlug)).then(() => setRefreshing(false));
}}
>
<RefreshCw className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{" "}

View File

@@ -94,7 +94,7 @@ export function JiraImportUsers() {
input
value={value}
onChange={onChange}
label={<span className="capitalize">{Boolean(value) ? value : ("Ignore" as any)}</span>}
label={<span className="capitalize">{value ? value : ("Ignore" as any)}</span>}
>
<CustomSelect.Option value="invite">Invite by email</CustomSelect.Option>
<CustomSelect.Option value="map">Map to existing</CustomSelect.Option>

View File

@@ -60,9 +60,8 @@ export const SingleIntegrationCard = observer(function SingleIntegrationCard({ i
slack_client_id: config?.slack_client_id || "",
});
const { data: workspaceIntegrations } = useSWR(
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
() => (workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
const { data: workspaceIntegrations } = useSWR(workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug) : null, () =>
workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug) : null
);
const handleRemoveIntegration = async () => {
@@ -73,10 +72,10 @@ export const SingleIntegrationCard = observer(function SingleIntegrationCard({ i
setDeletingIntegration(true);
await integrationService
.deleteWorkspaceIntegration(workspaceSlug as string, workspaceIntegrationId ?? "")
.deleteWorkspaceIntegration(workspaceSlug, workspaceIntegrationId ?? "")
.then(() => {
mutate<IWorkspaceIntegration[]>(
WORKSPACE_INTEGRATIONS(workspaceSlug as string),
WORKSPACE_INTEGRATIONS(workspaceSlug),
(prevData) => prevData?.filter((i) => i.id !== workspaceIntegrationId),
false
);

View File

@@ -38,16 +38,10 @@ export const SelectChannel = observer(function SelectChannel({ integration }: Pr
});
const { data: projectIntegration } = useSWR(
workspaceSlug && projectId && integration.id
? SLACK_CHANNEL_INFO(workspaceSlug as string, projectId as string)
: null,
workspaceSlug && projectId && integration.id ? SLACK_CHANNEL_INFO(workspaceSlug, projectId) : null,
() =>
workspaceSlug && projectId && integration.id
? appInstallationService.getSlackChannelDetail(
workspaceSlug as string,
projectId as string,
integration.id as string
)
? appInstallationService.getSlackChannelDetail(workspaceSlug, projectId, integration.id)
: null
);
@@ -74,7 +68,7 @@ export const SelectChannel = observer(function SelectChannel({ integration }: Pr
setSlackChannel(null);
});
appInstallationService
.removeSlackChannel(workspaceSlug as string, projectId as string, integration.id as string, slackChannel?.id)
.removeSlackChannel(workspaceSlug, projectId, integration.id, slackChannel?.id)
.catch((err) => console.error(err));
};

View File

@@ -186,7 +186,7 @@ export const RelationsCollapsibleContent = observer(function RelationsCollapsibl
issueCrudState.delete.issue.id &&
issueCrudState.delete.issue.project_id
) {
const deleteOperation = !!issueCrudState.delete.issue?.is_epic
const deleteOperation = issueCrudState.delete.issue?.is_epic
? epicOperations.remove
: issueOperations.remove;
await deleteOperation(
@@ -202,7 +202,7 @@ export const RelationsCollapsibleContent = observer(function RelationsCollapsibl
{shouldRenderIssueUpdateModal && (
<>
{!!issueCrudState?.update?.issue?.is_epic ? (
{issueCrudState?.update?.issue?.is_epic ? (
<CreateUpdateEpicModal
isOpen={issueCrudState?.update?.toggle}
onClose={() => {

View File

@@ -51,7 +51,7 @@ export const RelationActionButton = observer(function RelationActionButton(props
<CustomMenu.MenuItem
key={index}
onClick={() => {
handleOnClick(item.key as TIssueRelationTypes);
handleOnClick(item.key);
}}
>
<div className="flex items-center gap-2">

View File

@@ -13,7 +13,6 @@ import { useProjectInbox } from "@/hooks/store/use-project-inbox";
// types
import { LabelList, IssueLabelSelectRoot } from "./";
// TODO: Fix this import statement, as core should not import from ee
// eslint-disable-next-line import/order
export type TIssueLabel = {
workspaceSlug: string;

View File

@@ -39,12 +39,7 @@ import { CalendarWeekDays } from "./week-days";
import { CalendarWeekHeader } from "./week-header";
type Props = {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
issues: TIssueMap | undefined;
groupedIssueIds: TGroupedIssues;
layout: "month" | "week" | undefined;

View File

@@ -23,12 +23,7 @@ import type { TRenderQuickActions } from "../list/list-view-types";
import { CalendarIssueBlocks } from "./issue-blocks";
type Props = {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
date: ICalendarDate;
issues: TIssueMap | undefined;
groupedIssueIds: TGroupedIssues;

View File

@@ -17,12 +17,7 @@ import type { IProjectViewIssuesFilter } from "@/store/issue/project-views";
// helpers
interface Props {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
}
export const CalendarMonthsDropdown = observer(function CalendarMonthsDropdown(props: Props) {
const { issuesFilterStore } = props;

View File

@@ -25,12 +25,7 @@ import type { IProjectIssuesFilter } from "@/store/issue/project";
import type { IProjectViewIssuesFilter } from "@/store/issue/project-views";
interface ICalendarHeader {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
updateFilters?: (
projectId: string,
filterType: TSupportedFilterTypeForUpdate,

View File

@@ -16,12 +16,7 @@ import type { IProjectViewIssuesFilter } from "@/store/issue/project-views";
import { CalendarMonthsDropdown, CalendarOptionsDropdown } from "./dropdowns";
interface ICalendarHeader {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
updateFilters?: (
projectId: string,
filterType: TSupportedFilterTypeForUpdate,

View File

@@ -1,4 +1,3 @@
/* eslint-disable react/display-name */
import { useState, useRef, forwardRef } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";

View File

@@ -14,12 +14,7 @@ import type { TRenderQuickActions } from "../list/list-view-types";
import { CalendarDayTile } from "./day-tile";
type Props = {
issuesFilterStore:
| IProjectIssuesFilter
| IModuleIssuesFilter
| ICycleIssuesFilter
| IProjectViewIssuesFilter
| IProjectEpicsFilter;
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
issues: TIssueMap | undefined;
groupedIssueIds: TGroupedIssues;
week: ICalendarWeek | undefined;

View File

@@ -37,7 +37,7 @@ export const FilterDisplayProperties = observer(function FilterDisplayProperties
// states
const [previewEnabled, setPreviewEnabled] = React.useState(true);
// derived values
const projectId = !!routerProjectId ? routerProjectId?.toString() : undefined;
const projectId = routerProjectId ? routerProjectId?.toString() : undefined;
// Filter out "cycle" and "module" keys if cycleViewDisabled or moduleViewDisabled is true
// Also filter out display properties that should not be rendered

View File

@@ -152,7 +152,7 @@ export const KanBan = observer(function KanBan(props: IKanBan) {
const issueIds = isSubGroup
? ((groupedIssueIds as TSubGroupedIssues)?.[subList.id]?.[sub_group_id] ?? [])
: ((groupedIssueIds as TGroupedIssues)?.[subList.id] ?? []);
const issueLength = issueIds?.length as number;
const issueLength = issueIds?.length;
const groupHeight = issueLength * approximateCardHeight;
return (

View File

@@ -268,7 +268,7 @@ export const KanbanGroup = observer(function KanbanGroup(props: IKanbanGroup) {
const canDragIssuesInCurrentGrouping =
!!group_by &&
DRAG_ALLOWED_GROUPS.includes(group_by) &&
(!!sub_group_by ? DRAG_ALLOWED_GROUPS.includes(sub_group_by) : true);
(sub_group_by ? DRAG_ALLOWED_GROUPS.includes(sub_group_by) : true);
return (
<div

View File

@@ -160,7 +160,7 @@ const getCycleColumns = (): IGroupByColumn[] | undefined => {
cycles.push({
id: cycle.id,
name: cycle.name,
icon: <CycleGroupIcon cycleGroup={cycleStatus as TCycleGroups} className="h-3.5 w-3.5" />,
icon: <CycleGroupIcon cycleGroup={cycleStatus} className="h-3.5 w-3.5" />,
payload: { cycle_id: cycle.id },
isDropDisabled,
dropErrorMessage: isDropDisabled ? "Work item cannot be moved to completed cycles" : undefined,
@@ -486,11 +486,8 @@ const handleSortOrder = (
return currentIssueState;
};
export const getIssueBlockId = (
issueId: string | undefined,
groupId: string | undefined,
subGroupId?: string | undefined
) => `issue_${issueId}_${groupId}_${subGroupId}`;
export const getIssueBlockId = (issueId: string | undefined, groupId: string | undefined, subGroupId?: string) =>
`issue_${issueId}_${groupId}_${subGroupId}`;
/**
* returns empty Array if groupId is None

View File

@@ -180,7 +180,7 @@ export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(p
id="issue-modal-editor"
initialValue={value ?? ""}
value={descriptionHtmlData}
workspaceSlug={workspaceSlug?.toString() as string}
workspaceSlug={workspaceSlug?.toString()}
workspaceId={workspaceId}
projectId={projectId}
onChange={(_description: object, description_html: string) => {

View File

@@ -70,7 +70,7 @@ export function ParentIssuesListModal({
setIsLoading(true);
projectService
.projectIssuesSearch(workspaceSlug as string, projectId as string, {
.projectIssuesSearch(workspaceSlug, projectId, {
search: debouncedSearchTerm,
parent: searchEpic ? undefined : true,
issue_id: issueId,

View File

@@ -63,7 +63,7 @@ export const RelationIssueListItem = observer(function RelationIssueListItem(pro
// derived values
const issue = getIssueById(relationIssueId);
const { handleRedirection } = useIssuePeekOverviewRedirection(!!issue?.is_epic);
const issueOperations = useRelationOperations(!!issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES);
const issueOperations = useRelationOperations(issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES);
const projectDetail = (issue && issue.project_id && project.getProjectById(issue.project_id)) || undefined;
const projectId = issue?.project_id;

View File

@@ -150,7 +150,7 @@ export const LabelDndHOC = observer(function LabelDndHOC(props: Props) {
if (instruction === "reorder-below") dropAtEndOfList = true;
const sourceData = source.data as TargetData;
if (sourceData.id) onDrop(sourceData.id as string, parentId, droppedLabelId, dropAtEndOfList);
if (sourceData.id) onDrop(sourceData.id, parentId, droppedLabelId, dropAtEndOfList);
},
})
);

View File

@@ -78,6 +78,7 @@ export const TalkToSalesCard = observer(function TalkToSalesCard(props: TalkToSa
upgradeButtonStyle,
"relative inline-flex items-center justify-center w-56 px-4 py-2 text-sm font-medium rounded-lg focus:outline-none"
)}
rel="noreferrer"
>
Talk to Sales
</a>

View File

@@ -1,4 +1,3 @@
"use client";
import { observer } from "mobx-react";
import { useParams, usePathname } from "next/navigation";
import { Check, SettingsIcon } from "lucide-react";
@@ -14,7 +13,7 @@ import { DesktopSidebarWorkspaceMenu } from "@/plane-web/components/desktop";
// local imports
import { AppSidebarItemsRoot } from "./items-root";
export const AppRailRoot = observer(() => {
export const AppRailRoot = observer(function AppRailRoot() {
// router
const { workspaceSlug } = useParams();
const pathname = usePathname();

View File

@@ -38,7 +38,9 @@ const PERSONAL_ITEMS: Array<{ key: TPersonalNavigationItemKey; labelTranslationK
{ key: "drafts", labelTranslationKey: "drafts" },
];
export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = observer((props) => {
export const CustomizeNavigationDialog = observer(function CustomizeNavigationDialog(
props: TCustomizeNavigationDialogProps
) {
const { isOpen, onClose } = props;
const { t } = useTranslation();

View File

@@ -1,5 +1,4 @@
// components/AppSidebarItemsRoot.tsx
"use client";
import React from "react";
import type { AppSidebarItemData } from "@/components/sidebar/sidebar-item";
@@ -11,14 +10,16 @@ type Props = {
showLabel?: boolean;
};
const Component = ({ dockItems, showLabel = true }: Props) => (
<>
{dockItems
.filter((item) => item.shouldRender)
.map((item) => (
<AppSidebarItem key={item.label} item={{ ...item, showLabel }} variant="link" />
))}
</>
);
function Component({ dockItems, showLabel = true }: Props) {
return (
<>
{dockItems
.filter((item) => item.shouldRender)
.map((item) => (
<AppSidebarItem key={item.label} item={{ ...item, showLabel }} variant="link" />
))}
</>
);
}
export const AppSidebarItemsRoot = withDockItems(Component);

View File

@@ -1,5 +1,3 @@
"use client";
import type { FC } from "react";
import { useState, useRef } from "react";
import { useNavigate } from "react-router";
@@ -21,7 +19,7 @@ type Props = {
onPublishModal: () => void;
};
export const ProjectActionsMenu: FC<Props> = ({
export function ProjectActionsMenu({
workspaceSlug,
project,
isAdmin,
@@ -29,7 +27,7 @@ export const ProjectActionsMenu: FC<Props> = ({
onCopyText,
onLeaveProject,
onPublishModal,
}) => {
}: Props) {
// states
const [isMenuActive, setIsMenuActive] = useState(false);
// translation
@@ -111,4 +109,4 @@ export const ProjectActionsMenu: FC<Props> = ({
)}
</CustomMenu>
);
};
}

View File

@@ -22,7 +22,7 @@ type TProjectHeaderProps = {
projectId: string;
};
export const ProjectHeader = observer((props: TProjectHeaderProps) => {
export const ProjectHeader = observer(function ProjectHeader(props: TProjectHeaderProps) {
const { workspaceSlug, projectId } = props;
// router
const router = useAppRouter();

View File

@@ -24,13 +24,7 @@ type Props = {
* Displays items that don't fit in the visible area, with action icons
* Shows "Eye" icon for user-hidden items, "Set as default" icon for all items
*/
export const TabNavigationOverflowMenu: React.FC<Props> = ({
overflowItems,
isActive,
tabPreferences,
onToggleDefault,
onShow,
}) => {
export function TabNavigationOverflowMenu({ overflowItems, isActive, tabPreferences, onToggleDefault, onShow }: Props) {
const { t } = useTranslation();
return (
@@ -97,4 +91,4 @@ export const TabNavigationOverflowMenu: React.FC<Props> = ({
})}
</Menu>
);
};
}

View File

@@ -1,5 +1,3 @@
"use client";
import type { FC } from "react";
import React, { useEffect } from "react";
import { observer } from "mobx-react";
@@ -44,7 +42,7 @@ type TTabNavigationRootProps = {
projectId: string;
};
export const TabNavigationRoot: FC<TTabNavigationRootProps> = observer((props) => {
export const TabNavigationRoot = observer(function TabNavigationRoot(props: TTabNavigationRootProps) {
const { workspaceSlug, projectId } = props;
const { workItem: workItemIdentifierFromRoute } = useParams();
const location = useLocation();

View File

@@ -23,14 +23,14 @@ export type TTabNavigationVisibleItemProps = {
* Individual visible tab navigation item with context menu
* Handles right-click actions for setting default and hiding tabs
*/
export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps> = ({
export function TabNavigationVisibleItem({
item,
isActive,
tabPreferences,
onToggleDefault,
onHide,
itemRef,
}) => {
}: TTabNavigationVisibleItemProps) {
const { t } = useTranslation();
const isDefault = item.key === tabPreferences.defaultTab;
@@ -76,4 +76,4 @@ export const TabNavigationVisibleItem: React.FC<TTabNavigationVisibleItemProps>
</div>
</div>
);
};
}

View File

@@ -15,7 +15,7 @@ import { useUser } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { useExpandableSearch } from "@/hooks/use-expandable-search";
export const TopNavPowerK = observer(() => {
export const TopNavPowerK = observer(function TopNavPowerK() {
// router
const router = useAppRouter();
const params = useParams();

View File

@@ -87,108 +87,111 @@ export const PageActions = observer(function PageActions(props: Props) {
canCurrentUserMovePage,
} = page;
// menu items
const MENU_ITEMS = useMemo(() => {
const menuItems: (TContextMenuItem & { key: TPageActions })[] = [
{
key: "toggle-lock",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON,
});
pageOperations.toggleLock();
const MENU_ITEMS = useMemo(
function MENU_ITEMS() {
const menuItems: (TContextMenuItem & { key: TPageActions })[] = [
{
key: "toggle-lock",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.LOCK_BUTTON,
});
pageOperations.toggleLock();
},
title: is_locked ? "Unlock" : "Lock",
icon: is_locked ? LockKeyholeOpen : LockKeyhole,
shouldRender: canCurrentUserLockPage,
},
title: is_locked ? "Unlock" : "Lock",
icon: is_locked ? LockKeyholeOpen : LockKeyhole,
shouldRender: canCurrentUserLockPage,
},
{
key: "toggle-access",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.ACCESS_TOGGLE,
});
pageOperations.toggleAccess();
{
key: "toggle-access",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.ACCESS_TOGGLE,
});
pageOperations.toggleAccess();
},
title: access === EPageAccess.PUBLIC ? "Make private" : "Make public",
icon: access === EPageAccess.PUBLIC ? Lock : Globe2,
shouldRender: canCurrentUserChangeAccess && !archived_at,
},
title: access === EPageAccess.PUBLIC ? "Make private" : "Make public",
icon: access === EPageAccess.PUBLIC ? Lock : Globe2,
shouldRender: canCurrentUserChangeAccess && !archived_at,
},
{
key: "open-in-new-tab",
action: pageOperations.openInNewTab,
title: "Open in new tab",
icon: ExternalLink,
shouldRender: true,
},
{
key: "copy-link",
action: pageOperations.copyLink,
title: "Copy link",
icon: Link,
shouldRender: true,
},
{
key: "make-a-copy",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.DUPLICATE_BUTTON,
});
pageOperations.duplicate();
{
key: "open-in-new-tab",
action: pageOperations.openInNewTab,
title: "Open in new tab",
icon: ExternalLink,
shouldRender: true,
},
title: "Make a copy",
icon: Copy,
shouldRender: canCurrentUserDuplicatePage,
},
{
key: "archive-restore",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.ARCHIVE_BUTTON,
});
pageOperations.toggleArchive();
{
key: "copy-link",
action: pageOperations.copyLink,
title: "Copy link",
icon: Link,
shouldRender: true,
},
title: archived_at ? "Restore" : "Archive",
icon: archived_at ? ArchiveRestoreIcon : ArchiveIcon,
shouldRender: canCurrentUserArchivePage,
},
{
key: "delete",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.CONTEXT_MENU,
});
setDeletePageModal(true);
{
key: "make-a-copy",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.DUPLICATE_BUTTON,
});
pageOperations.duplicate();
},
title: "Make a copy",
icon: Copy,
shouldRender: canCurrentUserDuplicatePage,
},
title: "Delete",
icon: Trash2,
shouldRender: canCurrentUserDeletePage && !!archived_at,
},
{
key: "move",
action: () => setMovePageModal(true),
title: "Move",
icon: FileOutput,
shouldRender: canCurrentUserMovePage && isMovePageEnabled,
},
];
if (extraOptions) {
menuItems.push(...extraOptions);
}
return menuItems;
}, [
extraOptions,
is_locked,
canCurrentUserLockPage,
access,
canCurrentUserChangeAccess,
archived_at,
canCurrentUserDuplicatePage,
canCurrentUserArchivePage,
canCurrentUserDeletePage,
canCurrentUserMovePage,
isMovePageEnabled,
pageOperations,
]);
{
key: "archive-restore",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.ARCHIVE_BUTTON,
});
pageOperations.toggleArchive();
},
title: archived_at ? "Restore" : "Archive",
icon: archived_at ? ArchiveRestoreIcon : ArchiveIcon,
shouldRender: canCurrentUserArchivePage,
},
{
key: "delete",
action: () => {
captureClick({
elementName: PROJECT_PAGE_TRACKER_ELEMENTS.CONTEXT_MENU,
});
setDeletePageModal(true);
},
title: "Delete",
icon: Trash2,
shouldRender: canCurrentUserDeletePage && !!archived_at,
},
{
key: "move",
action: () => setMovePageModal(true),
title: "Move",
icon: FileOutput,
shouldRender: canCurrentUserMovePage && isMovePageEnabled,
},
];
if (extraOptions) {
menuItems.push(...extraOptions);
}
return menuItems;
},
[
extraOptions,
is_locked,
canCurrentUserLockPage,
access,
canCurrentUserChangeAccess,
archived_at,
canCurrentUserDuplicatePage,
canCurrentUserArchivePage,
canCurrentUserDeletePage,
canCurrentUserMovePage,
isMovePageEnabled,
pageOperations,
]
);
// arrange options
const arrangedOptions = useMemo<(TContextMenuItem & { key: TPageActions })[]>(
() =>

Some files were not shown because too many files have changed in this diff Show More