forked from github/plane
[WEB-756] chore: spreadsheet layout cycle and module feature toggle validation (#4121)
* chore: spreadsheet layout cycle and module feature toggle validation added * chore: project analytics cycle and module feature toggle validation added
This commit is contained in:
parent
71b73000d2
commit
b4cc58d5dd
@ -3,6 +3,7 @@ import { Control, Controller, UseFormSetValue } from "react-hook-form";
|
|||||||
import { IAnalyticsParams } from "@plane/types";
|
import { IAnalyticsParams } from "@plane/types";
|
||||||
// hooks
|
// hooks
|
||||||
import { SelectProject, SelectSegment, SelectXAxis, SelectYAxis } from "@/components/analytics";
|
import { SelectProject, SelectSegment, SelectXAxis, SelectYAxis } from "@/components/analytics";
|
||||||
|
import { ANALYTICS_X_AXIS_VALUES } from "@/constants/analytics";
|
||||||
import { useProject } from "@/hooks/store";
|
import { useProject } from "@/hooks/store";
|
||||||
// components
|
// components
|
||||||
// types
|
// types
|
||||||
@ -18,7 +19,15 @@ type Props = {
|
|||||||
export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
||||||
const { control, setValue, params, fullScreen, isProjectLevel } = props;
|
const { control, setValue, params, fullScreen, isProjectLevel } = props;
|
||||||
|
|
||||||
const { workspaceProjectIds: workspaceProjectIds } = useProject();
|
const { workspaceProjectIds: workspaceProjectIds, currentProjectDetails } = useProject();
|
||||||
|
|
||||||
|
const analyticsOptions = isProjectLevel
|
||||||
|
? ANALYTICS_X_AXIS_VALUES.filter((v) => {
|
||||||
|
if (v.value === "issue_cycle__cycle_id" && !currentProjectDetails?.cycle_view) return false;
|
||||||
|
if (v.value === "issue_module__module_id" && !currentProjectDetails?.module_view) return false;
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
: ANALYTICS_X_AXIS_VALUES;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -64,6 +73,7 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||||||
onChange(val);
|
onChange(val);
|
||||||
}}
|
}}
|
||||||
params={params}
|
params={params}
|
||||||
|
analyticsOptions={analyticsOptions}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -74,7 +84,7 @@ export const CustomAnalyticsSelectBar: React.FC<Props> = observer((props) => {
|
|||||||
name="segment"
|
name="segment"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<SelectSegment value={value} onChange={onChange} params={params} />
|
<SelectSegment value={value} onChange={onChange} params={params} analyticsOptions={analyticsOptions} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,17 +3,15 @@ import { IAnalyticsParams, TXAxisValues } from "@plane/types";
|
|||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { CustomSelect } from "@plane/ui";
|
||||||
// types
|
|
||||||
import { ANALYTICS_X_AXIS_VALUES } from "@/constants/analytics";
|
|
||||||
// constants
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: TXAxisValues | null | undefined;
|
value: TXAxisValues | null | undefined;
|
||||||
onChange: () => void;
|
onChange: () => void;
|
||||||
params: IAnalyticsParams;
|
params: IAnalyticsParams;
|
||||||
|
analyticsOptions: { value: TXAxisValues; label: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectSegment: React.FC<Props> = ({ value, onChange, params }) => {
|
export const SelectSegment: React.FC<Props> = ({ value, onChange, params, analyticsOptions }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { cycleId, moduleId } = router.query;
|
const { cycleId, moduleId } = router.query;
|
||||||
|
|
||||||
@ -22,7 +20,7 @@ export const SelectSegment: React.FC<Props> = ({ value, onChange, params }) => {
|
|||||||
value={value}
|
value={value}
|
||||||
label={
|
label={
|
||||||
<span>
|
<span>
|
||||||
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === value)?.label ?? (
|
{analyticsOptions.find((v) => v.value === value)?.label ?? (
|
||||||
<span className="text-custom-text-200">No value</span>
|
<span className="text-custom-text-200">No value</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@ -31,7 +29,7 @@ export const SelectSegment: React.FC<Props> = ({ value, onChange, params }) => {
|
|||||||
maxHeight="lg"
|
maxHeight="lg"
|
||||||
>
|
>
|
||||||
<CustomSelect.Option value={null}>No value</CustomSelect.Option>
|
<CustomSelect.Option value={null}>No value</CustomSelect.Option>
|
||||||
{ANALYTICS_X_AXIS_VALUES.map((item) => {
|
{analyticsOptions.map((item) => {
|
||||||
if (params.x_axis === item.value) return null;
|
if (params.x_axis === item.value) return null;
|
||||||
if (cycleId && item.value === "issue_cycle__cycle_id") return null;
|
if (cycleId && item.value === "issue_cycle__cycle_id") return null;
|
||||||
if (moduleId && item.value === "issue_module__module_id") return null;
|
if (moduleId && item.value === "issue_module__module_id") return null;
|
||||||
|
@ -3,18 +3,16 @@ import { IAnalyticsParams, TXAxisValues } from "@plane/types";
|
|||||||
|
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "@plane/ui";
|
import { CustomSelect } from "@plane/ui";
|
||||||
// types
|
|
||||||
import { ANALYTICS_X_AXIS_VALUES } from "@/constants/analytics";
|
|
||||||
// constants
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: TXAxisValues;
|
value: TXAxisValues;
|
||||||
onChange: (val: string) => void;
|
onChange: (val: string) => void;
|
||||||
params: IAnalyticsParams;
|
params: IAnalyticsParams;
|
||||||
|
analyticsOptions: { value: TXAxisValues; label: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectXAxis: React.FC<Props> = (props) => {
|
export const SelectXAxis: React.FC<Props> = (props) => {
|
||||||
const { value, onChange, params } = props;
|
const { value, onChange, params, analyticsOptions } = props;
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { cycleId, moduleId } = router.query;
|
const { cycleId, moduleId } = router.query;
|
||||||
@ -22,11 +20,11 @@ export const SelectXAxis: React.FC<Props> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
value={value}
|
value={value}
|
||||||
label={<span>{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === value)?.label}</span>}
|
label={<span>{analyticsOptions.find((v) => v.value === value)?.label}</span>}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
maxHeight="lg"
|
maxHeight="lg"
|
||||||
>
|
>
|
||||||
{ANALYTICS_X_AXIS_VALUES.map((item) => {
|
{analyticsOptions.map((item) => {
|
||||||
if (params.segment === item.value) return null;
|
if (params.segment === item.value) return null;
|
||||||
if (cycleId && item.value === "issue_cycle__cycle_id") return null;
|
if (cycleId && item.value === "issue_cycle__cycle_id") return null;
|
||||||
if (moduleId && item.value === "issue_module__module_id") return null;
|
if (moduleId && item.value === "issue_module__module_id") return null;
|
||||||
|
@ -182,6 +182,7 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
updateIssue={updateIssue}
|
updateIssue={updateIssue}
|
||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
viewId={globalViewId}
|
viewId={globalViewId}
|
||||||
|
isWorkspaceLevel
|
||||||
/>
|
/>
|
||||||
{/* peek overview */}
|
{/* peek overview */}
|
||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
|
@ -8,8 +8,6 @@ import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
|||||||
import { ControlLink, Tooltip } from "@plane/ui";
|
import { ControlLink, Tooltip } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import RenderIfVisible from "@/components/core/render-if-visible-HOC";
|
import RenderIfVisible from "@/components/core/render-if-visible-HOC";
|
||||||
// constants
|
|
||||||
import { SPREADSHEET_PROPERTY_LIST } from "@/constants/spreadsheet";
|
|
||||||
// helper
|
// helper
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
@ -37,6 +35,7 @@ interface Props {
|
|||||||
isScrolled: MutableRefObject<boolean>;
|
isScrolled: MutableRefObject<boolean>;
|
||||||
containerRef: MutableRefObject<HTMLTableElement | null>;
|
containerRef: MutableRefObject<HTMLTableElement | null>;
|
||||||
issueIds: string[];
|
issueIds: string[];
|
||||||
|
spreadsheetColumnsList: (keyof IIssueDisplayProperties)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SpreadsheetIssueRow = observer((props: Props) => {
|
export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||||
@ -52,6 +51,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
|||||||
isScrolled,
|
isScrolled,
|
||||||
containerRef,
|
containerRef,
|
||||||
issueIds,
|
issueIds,
|
||||||
|
spreadsheetColumnsList,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [isExpanded, setExpanded] = useState<boolean>(false);
|
const [isExpanded, setExpanded] = useState<boolean>(false);
|
||||||
@ -81,6 +81,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
|||||||
isScrolled={isScrolled}
|
isScrolled={isScrolled}
|
||||||
isExpanded={isExpanded}
|
isExpanded={isExpanded}
|
||||||
setExpanded={setExpanded}
|
setExpanded={setExpanded}
|
||||||
|
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||||
/>
|
/>
|
||||||
</RenderIfVisible>
|
</RenderIfVisible>
|
||||||
|
|
||||||
@ -101,6 +102,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
|||||||
isScrolled={isScrolled}
|
isScrolled={isScrolled}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
|
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
@ -123,6 +125,7 @@ interface IssueRowDetailsProps {
|
|||||||
isScrolled: MutableRefObject<boolean>;
|
isScrolled: MutableRefObject<boolean>;
|
||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
setExpanded: Dispatch<SetStateAction<boolean>>;
|
setExpanded: Dispatch<SetStateAction<boolean>>;
|
||||||
|
spreadsheetColumnsList: (keyof IIssueDisplayProperties)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||||
@ -138,6 +141,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
isScrolled,
|
isScrolled,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
setExpanded,
|
setExpanded,
|
||||||
|
spreadsheetColumnsList,
|
||||||
} = props;
|
} = props;
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -255,7 +259,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
|||||||
</ControlLink>
|
</ControlLink>
|
||||||
</td>
|
</td>
|
||||||
{/* Rest of the columns */}
|
{/* Rest of the columns */}
|
||||||
{SPREADSHEET_PROPERTY_LIST.map((property) => (
|
{spreadsheetColumnsList.map((property) => (
|
||||||
<IssueColumn
|
<IssueColumn
|
||||||
key={property}
|
key={property}
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// ui
|
// ui
|
||||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types";
|
import { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types";
|
||||||
import { LayersIcon } from "@plane/ui";
|
import { LayersIcon } from "@plane/ui";
|
||||||
// types
|
|
||||||
import { SPREADSHEET_PROPERTY_LIST } from "@/constants/spreadsheet";
|
|
||||||
// constants
|
// constants
|
||||||
// components
|
// components
|
||||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
@ -13,10 +11,12 @@ interface Props {
|
|||||||
displayFilters: IIssueDisplayFilterOptions;
|
displayFilters: IIssueDisplayFilterOptions;
|
||||||
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
handleDisplayFilterUpdate: (data: Partial<IIssueDisplayFilterOptions>) => void;
|
||||||
isEstimateEnabled: boolean;
|
isEstimateEnabled: boolean;
|
||||||
|
spreadsheetColumnsList: (keyof IIssueDisplayProperties)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SpreadsheetHeader = (props: Props) => {
|
export const SpreadsheetHeader = (props: Props) => {
|
||||||
const { displayProperties, displayFilters, handleDisplayFilterUpdate, isEstimateEnabled } = props;
|
const { displayProperties, displayFilters, handleDisplayFilterUpdate, isEstimateEnabled, spreadsheetColumnsList } =
|
||||||
|
props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<thead className="sticky top-0 left-0 z-[12] border-b-[0.5px] border-custom-border-100">
|
<thead className="sticky top-0 left-0 z-[12] border-b-[0.5px] border-custom-border-100">
|
||||||
@ -36,7 +36,7 @@ export const SpreadsheetHeader = (props: Props) => {
|
|||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
{SPREADSHEET_PROPERTY_LIST.map((property) => (
|
{spreadsheetColumnsList.map((property) => (
|
||||||
<SpreadsheetHeaderColumn
|
<SpreadsheetHeaderColumn
|
||||||
key={property}
|
key={property}
|
||||||
property={property}
|
property={property}
|
||||||
|
@ -22,6 +22,7 @@ type Props = {
|
|||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
portalElement: React.MutableRefObject<HTMLDivElement | null>;
|
portalElement: React.MutableRefObject<HTMLDivElement | null>;
|
||||||
containerRef: MutableRefObject<HTMLTableElement | null>;
|
containerRef: MutableRefObject<HTMLTableElement | null>;
|
||||||
|
spreadsheetColumnsList: (keyof IIssueDisplayProperties)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetTable = observer((props: Props) => {
|
export const SpreadsheetTable = observer((props: Props) => {
|
||||||
@ -36,6 +37,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
|||||||
updateIssue,
|
updateIssue,
|
||||||
canEditProperties,
|
canEditProperties,
|
||||||
containerRef,
|
containerRef,
|
||||||
|
spreadsheetColumnsList,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
@ -83,6 +85,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
|||||||
displayFilters={displayFilters}
|
displayFilters={displayFilters}
|
||||||
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
handleDisplayFilterUpdate={handleDisplayFilterUpdate}
|
||||||
isEstimateEnabled={isEstimateEnabled}
|
isEstimateEnabled={isEstimateEnabled}
|
||||||
|
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||||
/>
|
/>
|
||||||
<tbody>
|
<tbody>
|
||||||
{issueIds.map((id) => (
|
{issueIds.map((id) => (
|
||||||
@ -99,6 +102,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
|||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
isScrolled={isScrolled}
|
isScrolled={isScrolled}
|
||||||
issueIds={issueIds}
|
issueIds={issueIds}
|
||||||
|
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -4,6 +4,7 @@ import { TIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@pl
|
|||||||
// components
|
// components
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
import { SpreadsheetQuickAddIssueForm } from "@/components/issues";
|
import { SpreadsheetQuickAddIssueForm } from "@/components/issues";
|
||||||
|
import { SPREADSHEET_PROPERTY_LIST } from "@/constants/spreadsheet";
|
||||||
import { useProject } from "@/hooks/store";
|
import { useProject } from "@/hooks/store";
|
||||||
import { SpreadsheetTable } from "./spreadsheet-table";
|
import { SpreadsheetTable } from "./spreadsheet-table";
|
||||||
// types
|
// types
|
||||||
@ -31,6 +32,7 @@ type Props = {
|
|||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
enableQuickCreateIssue?: boolean;
|
enableQuickCreateIssue?: boolean;
|
||||||
disableIssueCreation?: boolean;
|
disableIssueCreation?: boolean;
|
||||||
|
isWorkspaceLevel?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||||
@ -46,6 +48,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
canEditProperties,
|
canEditProperties,
|
||||||
enableQuickCreateIssue,
|
enableQuickCreateIssue,
|
||||||
disableIssueCreation,
|
disableIssueCreation,
|
||||||
|
isWorkspaceLevel = false,
|
||||||
} = props;
|
} = props;
|
||||||
// refs
|
// refs
|
||||||
const containerRef = useRef<HTMLTableElement | null>(null);
|
const containerRef = useRef<HTMLTableElement | null>(null);
|
||||||
@ -55,6 +58,14 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
const isEstimateEnabled: boolean = currentProjectDetails?.estimate !== null;
|
const isEstimateEnabled: boolean = currentProjectDetails?.estimate !== null;
|
||||||
|
|
||||||
|
const spreadsheetColumnsList = isWorkspaceLevel
|
||||||
|
? SPREADSHEET_PROPERTY_LIST
|
||||||
|
: SPREADSHEET_PROPERTY_LIST.filter((property) => {
|
||||||
|
if (property === "cycle" && !currentProjectDetails?.cycle_view) return false;
|
||||||
|
if (property === "modules" && !currentProjectDetails?.module_view) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
if (!issueIds || issueIds.length === 0)
|
if (!issueIds || issueIds.length === 0)
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full w-full place-items-center">
|
<div className="grid h-full w-full place-items-center">
|
||||||
@ -77,6 +88,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
updateIssue={updateIssue}
|
updateIssue={updateIssue}
|
||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
|
spreadsheetColumnsList={spreadsheetColumnsList}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-custom-border-100">
|
<div className="border-t border-custom-border-100">
|
||||||
|
Loading…
Reference in New Issue
Block a user