fix: Added a common dropdown component (#5826)
* fix: Added a common dropdown component * fix: dropdown * fix: estimate dropdown * fix: removed consoles
This commit is contained in:
parent
8d0611b2a7
commit
645a261493
5 changed files with 64 additions and 55 deletions
|
|
@ -3,15 +3,14 @@ import { FC, Fragment } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane ui
|
// plane ui
|
||||||
import { TCycleEstimateType } from "@plane/types";
|
import { TCycleEstimateType } from "@plane/types";
|
||||||
import { EEstimateSystem } from "@plane/types/src/enums";
|
import { Loader } from "@plane/ui";
|
||||||
import { CustomSelect, Loader } from "@plane/ui";
|
|
||||||
// components
|
// components
|
||||||
import ProgressChart from "@/components/core/sidebar/progress-chart";
|
import ProgressChart from "@/components/core/sidebar/progress-chart";
|
||||||
import { cycleEstimateOptions, validateCycleSnapshot } from "@/components/cycles";
|
import { EstimateTypeDropdown, validateCycleSnapshot } from "@/components/cycles";
|
||||||
// helpers
|
// helpers
|
||||||
import { getDate } from "@/helpers/date-time.helper";
|
import { getDate } from "@/helpers/date-time.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle, useProjectEstimates } from "@/hooks/store";
|
import { useCycle } from "@/hooks/store";
|
||||||
|
|
||||||
type ProgressChartProps = {
|
type ProgressChartProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -24,7 +23,6 @@ export const SidebarChart: FC<ProgressChartProps> = observer((props) => {
|
||||||
// hooks
|
// hooks
|
||||||
const { getEstimateTypeByCycleId, getCycleById, fetchCycleDetails, fetchArchivedCycleDetails, setEstimateType } =
|
const { getEstimateTypeByCycleId, getCycleById, fetchCycleDetails, fetchArchivedCycleDetails, setEstimateType } =
|
||||||
useCycle();
|
useCycle();
|
||||||
const { currentActiveEstimateId, areEstimateEnabledByProjectId, estimateById } = useProjectEstimates();
|
|
||||||
|
|
||||||
// derived data
|
// derived data
|
||||||
const cycleDetails = validateCycleSnapshot(getCycleById(cycleId));
|
const cycleDetails = validateCycleSnapshot(getCycleById(cycleId));
|
||||||
|
|
@ -33,10 +31,7 @@ export const SidebarChart: FC<ProgressChartProps> = observer((props) => {
|
||||||
const totalEstimatePoints = cycleDetails?.total_estimate_points || 0;
|
const totalEstimatePoints = cycleDetails?.total_estimate_points || 0;
|
||||||
const totalIssues = cycleDetails?.total_issues || 0;
|
const totalIssues = cycleDetails?.total_issues || 0;
|
||||||
const estimateType = getEstimateTypeByCycleId(cycleId);
|
const estimateType = getEstimateTypeByCycleId(cycleId);
|
||||||
const isCurrentProjectEstimateEnabled = Boolean(projectId && areEstimateEnabledByProjectId(projectId));
|
|
||||||
const estimateDetails =
|
|
||||||
isCurrentProjectEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId);
|
|
||||||
const isCurrentEstimateTypeIsPoints = estimateDetails && estimateDetails?.type === EEstimateSystem.POINTS;
|
|
||||||
const chartDistributionData =
|
const chartDistributionData =
|
||||||
estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined;
|
estimateType === "points" ? cycleDetails?.estimate_distribution : cycleDetails?.distribution || undefined;
|
||||||
|
|
||||||
|
|
@ -63,23 +58,9 @@ export const SidebarChart: FC<ProgressChartProps> = observer((props) => {
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isCurrentEstimateTypeIsPoints && (
|
|
||||||
<div className="relative flex items-center justify-between gap-2 pt-4">
|
<div className="relative flex items-center justify-between gap-2 pt-4">
|
||||||
<CustomSelect
|
<EstimateTypeDropdown value={estimateType} onChange={onChange} cycleId={cycleId} projectId={projectId} />
|
||||||
value={estimateType}
|
|
||||||
label={<span>{cycleEstimateOptions.find((v) => v.value === estimateType)?.label ?? "None"}</span>}
|
|
||||||
onChange={onChange}
|
|
||||||
maxHeight="lg"
|
|
||||||
buttonClassName="border-none rounded text-sm font-medium capitalize"
|
|
||||||
>
|
|
||||||
{cycleEstimateOptions.map((item) => (
|
|
||||||
<CustomSelect.Option key={item.value} value={item.value} className="capitalize">
|
|
||||||
{item.label}
|
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { FC, Fragment } from "react";
|
import { FC, Fragment } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ICycle, TCycleEstimateType, TCyclePlotType } from "@plane/types";
|
import { ICycle, TCycleEstimateType } from "@plane/types";
|
||||||
import { CustomSelect, Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import ProgressChart from "@/components/core/sidebar/progress-chart";
|
import ProgressChart from "@/components/core/sidebar/progress-chart";
|
||||||
import { EmptyState } from "@/components/empty-state";
|
import { EmptyState } from "@/components/empty-state";
|
||||||
|
|
@ -11,6 +11,7 @@ import { EmptyStateType } from "@/constants/empty-state";
|
||||||
import { useCycle, useProjectEstimates } from "@/hooks/store";
|
import { useCycle, useProjectEstimates } from "@/hooks/store";
|
||||||
// plane web constants
|
// plane web constants
|
||||||
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
||||||
|
import { EstimateTypeDropdown } from "../dropdowns/estimate-type-dropdown";
|
||||||
|
|
||||||
export type ActiveCycleProductivityProps = {
|
export type ActiveCycleProductivityProps = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -18,16 +19,10 @@ export type ActiveCycleProductivityProps = {
|
||||||
cycle: ICycle | null;
|
cycle: ICycle | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cycleBurnDownChartOptions = [
|
|
||||||
{ value: "issues", label: "Issues" },
|
|
||||||
{ value: "points", label: "Points" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ActiveCycleProductivity: FC<ActiveCycleProductivityProps> = observer((props) => {
|
export const ActiveCycleProductivity: FC<ActiveCycleProductivityProps> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, cycle } = props;
|
const { workspaceSlug, projectId, cycle } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const { getEstimateTypeByCycleId, setEstimateType } = useCycle();
|
const { getEstimateTypeByCycleId, setEstimateType } = useCycle();
|
||||||
const { currentActiveEstimateId, areEstimateEnabledByProjectId, estimateById } = useProjectEstimates();
|
|
||||||
|
|
||||||
// derived values
|
// derived values
|
||||||
const estimateType: TCycleEstimateType = (cycle && getEstimateTypeByCycleId(cycle.id)) || "issues";
|
const estimateType: TCycleEstimateType = (cycle && getEstimateTypeByCycleId(cycle.id)) || "issues";
|
||||||
|
|
@ -37,11 +32,6 @@ export const ActiveCycleProductivity: FC<ActiveCycleProductivityProps> = observe
|
||||||
setEstimateType(cycle.id, value);
|
setEstimateType(cycle.id, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCurrentProjectEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId) ? true : false;
|
|
||||||
const estimateDetails =
|
|
||||||
isCurrentProjectEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId);
|
|
||||||
const isCurrentEstimateTypeIsPoints = estimateDetails && estimateDetails?.type === EEstimateSystem.POINTS;
|
|
||||||
|
|
||||||
const chartDistributionData =
|
const chartDistributionData =
|
||||||
cycle && estimateType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined;
|
cycle && estimateType === "points" ? cycle?.estimate_distribution : cycle?.distribution || undefined;
|
||||||
const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;
|
const completionChartDistributionData = chartDistributionData?.completion_chart || undefined;
|
||||||
|
|
@ -52,22 +42,7 @@ export const ActiveCycleProductivity: FC<ActiveCycleProductivityProps> = observe
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle?.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle?.id}`}>
|
||||||
<h3 className="text-base text-custom-text-300 font-semibold">Issue burndown</h3>
|
<h3 className="text-base text-custom-text-300 font-semibold">Issue burndown</h3>
|
||||||
</Link>
|
</Link>
|
||||||
{isCurrentEstimateTypeIsPoints && (
|
<EstimateTypeDropdown value={estimateType} onChange={onChange} cycleId={cycle.id} projectId={projectId} />
|
||||||
<div className="relative flex items-center gap-2">
|
|
||||||
<CustomSelect
|
|
||||||
value={estimateType}
|
|
||||||
label={<span>{cycleBurnDownChartOptions.find((v) => v.value === estimateType)?.label ?? "None"}</span>}
|
|
||||||
onChange={onChange}
|
|
||||||
maxHeight="lg"
|
|
||||||
>
|
|
||||||
{cycleBurnDownChartOptions.map((item) => (
|
|
||||||
<CustomSelect.Option key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</CustomSelect.Option>
|
|
||||||
))}
|
|
||||||
</CustomSelect>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle?.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle?.id}`}>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
import { TCycleEstimateType } from "@plane/types";
|
||||||
|
import { CustomSelect } from "@plane/ui";
|
||||||
|
import { useCycle, useProjectEstimates } from "@/hooks/store";
|
||||||
|
import { cycleEstimateOptions } from "../analytics-sidebar";
|
||||||
|
|
||||||
|
type TProps = {
|
||||||
|
value: TCycleEstimateType;
|
||||||
|
onChange: (value: TCycleEstimateType) => Promise<void>;
|
||||||
|
showDefault?: boolean;
|
||||||
|
projectId: string;
|
||||||
|
cycleId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EstimateTypeDropdown = (props: TProps) => {
|
||||||
|
const { value, onChange, projectId, cycleId, showDefault = false } = props;
|
||||||
|
const { getIsPointsDataAvailable } = useCycle();
|
||||||
|
const { areEstimateEnabledByProjectId } = useProjectEstimates();
|
||||||
|
const isCurrentProjectEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId) ? true : false;
|
||||||
|
return getIsPointsDataAvailable(cycleId) || isCurrentProjectEstimateEnabled ? (
|
||||||
|
<div className="relative flex items-center gap-2">
|
||||||
|
<CustomSelect
|
||||||
|
value={value}
|
||||||
|
label={<span>{cycleEstimateOptions.find((v) => v.value === value)?.label ?? "None"}</span>}
|
||||||
|
onChange={onChange}
|
||||||
|
maxHeight="lg"
|
||||||
|
buttonClassName="bg-custom-background-90 border-none rounded text-sm font-medium "
|
||||||
|
>
|
||||||
|
{cycleEstimateOptions.map((item) => (
|
||||||
|
<CustomSelect.Option key={item.value} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</CustomSelect.Option>
|
||||||
|
))}
|
||||||
|
</CustomSelect>
|
||||||
|
</div>
|
||||||
|
) : showDefault ? (
|
||||||
|
<span className="capitalize">{value}</span>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export * from "./filters";
|
export * from "./filters";
|
||||||
|
export * from "./estimate-type-dropdown";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { isFuture, isPast, isToday } from "date-fns";
|
import { isFuture, isPast, isToday } from "date-fns";
|
||||||
|
import isEmpty from "lodash/isEmpty";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
||||||
|
|
@ -58,6 +59,8 @@ export interface ICycleStore {
|
||||||
getProjectCycleIds: (projectId: string) => string[] | null;
|
getProjectCycleIds: (projectId: string) => string[] | null;
|
||||||
getPlotTypeByCycleId: (cycleId: string) => TCyclePlotType;
|
getPlotTypeByCycleId: (cycleId: string) => TCyclePlotType;
|
||||||
getEstimateTypeByCycleId: (cycleId: string) => TCycleEstimateType;
|
getEstimateTypeByCycleId: (cycleId: string) => TCycleEstimateType;
|
||||||
|
getIsPointsDataAvailable: (cycleId: string) => boolean;
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
updateCycleDistribution: (distributionUpdates: DistributionUpdates, cycleId: string) => void;
|
updateCycleDistribution: (distributionUpdates: DistributionUpdates, cycleId: string) => void;
|
||||||
validateDate: (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => Promise<any>;
|
validateDate: (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => Promise<any>;
|
||||||
|
|
@ -271,6 +274,16 @@ export class CycleStore implements ICycleStore {
|
||||||
return this.cycleMap?.[this.currentProjectActiveCycleId!] ?? null;
|
return this.cycleMap?.[this.currentProjectActiveCycleId!] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsPointsDataAvailable = computedFn((cycleId: string) => {
|
||||||
|
const cycle = this.cycleMap[cycleId];
|
||||||
|
if (!cycle) return false;
|
||||||
|
if (this.cycleMap[cycleId].version === 2) return cycle.progress.some((p) => p.total_estimate_points > 0);
|
||||||
|
else if (this.cycleMap[cycleId].version === 1) {
|
||||||
|
const completionChart = cycle.estimate_distribution?.completion_chart || {};
|
||||||
|
return !isEmpty(completionChart) && Object.keys(completionChart).some((p) => completionChart[p]! > 0);
|
||||||
|
} else return false;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns active cycle progress for a project
|
* returns active cycle progress for a project
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue