Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions apps/web/ce/components/estimates/estimate-list-item-buttons.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { FC } from "react";
import { observer } from "mobx-react";
import { Pen, Trash } from "lucide-react";
import { Trash } from "lucide-react";
import { PROJECT_SETTINGS_TRACKER_ELEMENTS } from "@plane/constants";
import { Tooltip } from "@plane/propel/tooltip";
// components
import { ProIcon } from "@/components/common/pro-icon";

type TEstimateListItem = {
estimateId: string;
Expand All @@ -21,22 +17,6 @@ export const EstimateListItemButtons = observer(function EstimateListItemButtons
if (!isAdmin || !isEditable) return <></>;
return (
<div className="relative flex items-center gap-1">
<Tooltip
tooltipContent={
<div className="relative flex items-center gap-2">
<div>Upgrade</div>
<ProIcon className="w-3 h-3" />
</div>
}
position="top"
>
<button
className="relative flex-shrink-0 w-6 h-6 flex justify-center items-center rounded cursor-pointer transition-colors overflow-hidden hover:bg-custom-background-80"
data-ph-element={PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_LIST_ITEM}
>
<Pen size={12} />
</button>
</Tooltip>
<button
className="relative flex-shrink-0 w-6 h-6 flex justify-center items-center rounded cursor-pointer transition-colors overflow-hidden hover:bg-custom-background-80"
onClick={() => onDeleteClick && onDeleteClick(estimateId)}
Expand Down
30 changes: 0 additions & 30 deletions apps/web/ce/constants/project/settings/features.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ReactNode } from "react";
import { Timer } from "lucide-react";
// plane imports
import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, ViewsIcon } from "@plane/propel/icons";
import type { IProject } from "@plane/types";
Expand All @@ -17,7 +16,6 @@ export type TProperties = {
};

type TProjectBaseFeatureKeys = "cycles" | "modules" | "views" | "pages" | "inbox";
type TProjectOtherFeatureKeys = "is_time_tracking_enabled";

type TBaseFeatureList = {
[key in TProjectBaseFeatureKeys]: TProperties;
Expand Down Expand Up @@ -71,35 +69,13 @@ export const PROJECT_BASE_FEATURES_LIST: TBaseFeatureList = {
},
};

type TOtherFeatureList = {
[key in TProjectOtherFeatureKeys]: TProperties;
};

export const PROJECT_OTHER_FEATURES_LIST: TOtherFeatureList = {
is_time_tracking_enabled: {
key: "time_tracking",
property: "is_time_tracking_enabled",
title: "Time Tracking",
description: "Log time, see timesheets, and download full CSVs for your entire workspace.",
icon: <Timer className="h-5 w-5 flex-shrink-0 text-custom-text-300" />,
isPro: true,
isEnabled: false,
},
};

type TProjectFeatures = {
project_features: {
key: string;
title: string;
description: string;
featureList: TBaseFeatureList;
};
project_others: {
key: string;
title: string;
description: string;
featureList: TOtherFeatureList;
};
};

export const PROJECT_FEATURES_LIST: TProjectFeatures = {
Expand All @@ -109,10 +85,4 @@ export const PROJECT_FEATURES_LIST: TProjectFeatures = {
description: "Toggle these on or off this project.",
featureList: PROJECT_BASE_FEATURES_LIST,
},
project_others: {
key: "work_management",
title: "Work management",
description: "Available only on some plans as indicated by the label next to the feature below.",
featureList: PROJECT_OTHER_FEATURES_LIST,
},
};
52 changes: 27 additions & 25 deletions apps/web/core/components/estimates/create/stage-one.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { FC } from "react";
import { Info } from "lucide-react";
// plane imports
import { EEstimateSystem, ESTIMATE_SYSTEMS } from "@plane/constants";
Expand Down Expand Up @@ -32,29 +31,32 @@ export function EstimateCreateStageOne(props: TEstimateCreateStageOne) {
<div className="space-y-6">
<div className="sm:flex sm:items-center sm:space-x-10 sm:space-y-0 gap-2 mb-2">
<RadioInput
options={Object.keys(ESTIMATE_SYSTEMS).map((system) => {
const currentSystem = system as TEstimateSystemKeys;
const isEnabled = isEstimateSystemEnabled(currentSystem);
return {
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<Tooltip tooltipContent={t("common.coming_soon")}>
<Info size={12} />
</Tooltip>
</div>
) : !isEnabled ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<UpgradeBadge />
</div>
) : (
<div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
),
value: system,
disabled: !isEnabled,
};
})}
options={Object.keys(ESTIMATE_SYSTEMS)
.map((system) => {
const currentSystem = system as TEstimateSystemKeys;
const isEnabled = isEstimateSystemEnabled(currentSystem);
if (!isEnabled) return null;
return {
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<Tooltip tooltipContent={t("common.coming_soon")}>
<Info size={12} />
</Tooltip>
</div>
) : !isEnabled ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<UpgradeBadge />
</div>
) : (
<div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
),
value: system,
disabled: !isEnabled,
};
})
.filter((option) => option !== null)}
Comment on lines +34 to +59
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove unreachable code after early return.

Lines 47-51 and the disabled property on line 56 contain dead code. Since line 38 returns null when !isEnabled, the subsequent check for !isEnabled on line 47 will never be true, and disabled: !isEnabled will always evaluate to false.

Apply this diff to remove the unreachable code:

 options={Object.keys(ESTIMATE_SYSTEMS)
   .map((system) => {
     const currentSystem = system as TEstimateSystemKeys;
     const isEnabled = isEstimateSystemEnabled(currentSystem);
     if (!isEnabled) return null;
     return {
       label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
         <div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
           {t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
           <Tooltip tooltipContent={t("common.coming_soon")}>
             <Info size={12} />
           </Tooltip>
         </div>
-      ) : !isEnabled ? (
-        <div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
-          {t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
-          <UpgradeBadge />
-        </div>
       ) : (
         <div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
       ),
       value: system,
-      disabled: !isEnabled,
+      disabled: false,
     };
   })
   .filter((option) => option !== null)}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
options={Object.keys(ESTIMATE_SYSTEMS)
.map((system) => {
const currentSystem = system as TEstimateSystemKeys;
const isEnabled = isEstimateSystemEnabled(currentSystem);
if (!isEnabled) return null;
return {
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<Tooltip tooltipContent={t("common.coming_soon")}>
<Info size={12} />
</Tooltip>
</div>
) : !isEnabled ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<UpgradeBadge />
</div>
) : (
<div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
),
value: system,
disabled: !isEnabled,
};
})
.filter((option) => option !== null)}
options={Object.keys(ESTIMATE_SYSTEMS)
.map((system) => {
const currentSystem = system as TEstimateSystemKeys;
const isEnabled = isEstimateSystemEnabled(currentSystem);
if (!isEnabled) return null;
return {
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}
<Tooltip tooltipContent={t("common.coming_soon")}>
<Info size={12} />
</Tooltip>
</div>
) : (
<div>{t(ESTIMATE_SYSTEMS[currentSystem]?.i18n_name)}</div>
),
value: system,
disabled: false,
};
})
.filter((option) => option !== null)}
🤖 Prompt for AI Agents
In apps/web/core/components/estimates/create/stage-one.tsx around lines 34 to
59, remove the unreachable conditional and dead props: because you return null
when !isEnabled (line ~38), delete the inner `: !isEnabled ? ...` branch used
for the label (lines ~47-51) and remove the `disabled: !isEnabled` property
(line ~56). Simplify the label to only handle the available vs coming-soon cases
(available -> plain label, not available -> coming soon with Tooltip/Info), and
keep the outer filter to remove nulls (e.g., filter(Boolean)) so options are
only enabled systems.

name="estimate-radio-input"
label={t("project_settings.estimates.create.choose_estimate_system")}
labelClassName="text-sm font-medium text-custom-text-200 mb-1.5"
Expand Down Expand Up @@ -99,7 +101,7 @@ export function EstimateCreateStageOne(props: TEstimateCreateStageOne) {
<p className="text-xs text-custom-text-300">
{currentEstimateSystem.templates[name]?.values
?.map((template) =>
estimateSystem === EEstimateSystem.TIME
estimateSystem === (EEstimateSystem.TIME as TEstimateSystemKeys)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the EEstimateSystem enum definition
echo "=== EEstimateSystem enum definition ==="
rg -n "enum EEstimateSystem" -A 15

# Find the TEstimateSystemKeys type definition
echo -e "\n=== TEstimateSystemKeys type definition ==="
rg -n "type TEstimateSystemKeys" -A 10

# Find other usages of EEstimateSystem.TIME
echo -e "\n=== Other usages of EEstimateSystem.TIME ==="
rg -n "EEstimateSystem\.TIME" -C 3

Repository: makeplane/plane

Length of output: 13521


🏁 Script executed:

#!/bin/bash
# Check the estimateSystem variable declaration and type in stage-one.tsx
echo "=== estimateSystem variable in stage-one.tsx ==="
rg -n "estimateSystem" apps/web/core/components/estimates/create/stage-one.tsx -B 3 -A 3 | head -50

Repository: makeplane/plane

Length of output: 1728


Remove the unnecessary type cast in the enum comparison.

The cast (EEstimateSystem.TIME as TEstimateSystemKeys) is redundant. Since estimateSystem is already typed as TEstimateSystemKeys and EEstimateSystem.TIME is a member of that union, the comparison works without explicit casting. Simplify to: estimateSystem === EEstimateSystem.TIME. This aligns with the pattern used throughout the codebase and reduces unnecessary type assertions.

🤖 Prompt for AI Agents
In apps/web/core/components/estimates/create/stage-one.tsx around line 104,
remove the unnecessary type cast in the enum comparison: replace the expression
using (EEstimateSystem.TIME as TEstimateSystemKeys) with a direct comparison to
EEstimateSystem.TIME since estimateSystem is already typed as
TEstimateSystemKeys; update the line to use estimateSystem ===
EEstimateSystem.TIME and ensure no other casts remain in that comparison.

? convertMinutesToHoursMinutesString(Number(template.value)).trim()
: template.value
)
Expand Down
5 changes: 3 additions & 2 deletions apps/web/core/components/project/settings/features-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ProjectFeaturesList = observer(function ProjectFeaturesList(props:
// derived values
const currentProjectDetails = getProjectById(projectId);

const handleSubmit = async (featureKey: string, featureProperty: string) => {
const handleSubmit = (featureKey: string, featureProperty: string) => {
if (!workspaceSlug || !projectId || !currentProjectDetails) return;

// making the request to update the project feature
Expand All @@ -52,13 +52,14 @@ export const ProjectFeaturesList = observer(function ProjectFeaturesList(props:
message: () => "Something went wrong while updating project feature. Please try again.",
},
});
updateProjectPromise.then(() => {
void updateProjectPromise.then(() => {
captureSuccess({
eventName: PROJECT_TRACKER_EVENTS.feature_toggled,
payload: {
feature_key: featureKey,
},
});
return undefined;
});
};

Expand Down
Loading