forked from github/plane
fix: corrected rendering of workspace-level labels, members, and states in project view (#2966)
* fix: dynamic issue properties filters in project, workspace and profile level * clean-up: removed logs from the project store
This commit is contained in:
parent
332e56bb0d
commit
8c462f96ee
@ -89,6 +89,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
||||
<IssuePropertyState
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.state || null}
|
||||
defaultOptions={issue?.state_detail ? [issue.state_detail] : []}
|
||||
onChange={handleState}
|
||||
disabled={isReadOnly}
|
||||
hideDropdownArrow
|
||||
@ -110,6 +111,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
||||
<IssuePropertyLabels
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.labels || null}
|
||||
defaultOptions={issue?.label_details ? issue.label_details : []}
|
||||
onChange={handleLabel}
|
||||
disabled={isReadOnly}
|
||||
hideDropdownArrow
|
||||
@ -141,6 +143,7 @@ export const KanBanProperties: React.FC<IKanBanProperties> = observer((props) =>
|
||||
<IssuePropertyAssignee
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.assignees || null}
|
||||
defaultOptions={issue?.assignee_details ? issue.assignee_details : []}
|
||||
hideDropdownArrow
|
||||
onChange={handleAssignee}
|
||||
disabled={isReadOnly}
|
||||
|
@ -60,6 +60,7 @@ export const ListProperties: FC<IListProperties> = observer((props) => {
|
||||
<IssuePropertyState
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.state || null}
|
||||
defaultOptions={issue?.state_detail ? [issue.state_detail] : []}
|
||||
hideDropdownArrow
|
||||
onChange={handleState}
|
||||
disabled={isReadonly}
|
||||
@ -81,6 +82,7 @@ export const ListProperties: FC<IListProperties> = observer((props) => {
|
||||
<IssuePropertyLabels
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.labels || null}
|
||||
defaultOptions={issue?.label_details ? issue.label_details : []}
|
||||
onChange={handleLabel}
|
||||
disabled={isReadonly}
|
||||
hideDropdownArrow
|
||||
@ -92,6 +94,7 @@ export const ListProperties: FC<IListProperties> = observer((props) => {
|
||||
<IssuePropertyAssignee
|
||||
projectId={issue?.project_detail?.id || null}
|
||||
value={issue?.assignees || null}
|
||||
defaultOptions={issue?.assignee_details ? issue.assignee_details : []}
|
||||
hideDropdownArrow
|
||||
onChange={handleAssignee}
|
||||
disabled={isReadonly}
|
||||
|
@ -8,10 +8,12 @@ import { Check, ChevronDown, Search, User2 } from "lucide-react";
|
||||
import { Avatar, AvatarGroup, Tooltip } from "@plane/ui";
|
||||
// types
|
||||
import { Placement } from "@popperjs/core";
|
||||
import { IProjectMember } from "types";
|
||||
|
||||
export interface IIssuePropertyAssignee {
|
||||
projectId: string | null;
|
||||
value: string[] | string;
|
||||
defaultOptions?: any;
|
||||
onChange: (data: string[]) => void;
|
||||
disabled?: boolean;
|
||||
hideDropdownArrow?: boolean;
|
||||
@ -27,6 +29,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
const {
|
||||
projectId,
|
||||
value,
|
||||
defaultOptions = [],
|
||||
onChange,
|
||||
disabled = false,
|
||||
hideDropdownArrow = false,
|
||||
@ -40,8 +43,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
// store
|
||||
const {
|
||||
workspace: workspaceStore,
|
||||
project: projectStore,
|
||||
workspaceMember: { workspaceMembers, fetchWorkspaceMembers },
|
||||
projectMember: { projectMembers: _projectMembers, fetchProjectMembers },
|
||||
} = useMobxStore();
|
||||
const workspaceSlug = workspaceStore?.workspaceSlug;
|
||||
// states
|
||||
@ -50,20 +52,16 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<Boolean>(false);
|
||||
|
||||
// const fetchProjectMembers = () => {
|
||||
// setIsLoading(true);
|
||||
// if (workspaceSlug && projectId)
|
||||
// workspaceSlug &&
|
||||
// projectId &&
|
||||
// projectStore.fetchProjectMembers(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||
// };
|
||||
|
||||
const getWorkspaceMembers = () => {
|
||||
setIsLoading(true);
|
||||
if (workspaceSlug) workspaceSlug && fetchWorkspaceMembers(workspaceSlug).then(() => setIsLoading(false));
|
||||
if (workspaceSlug && projectId) fetchProjectMembers(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
const options = (workspaceMembers ?? [])?.map((member) => ({
|
||||
const updatedDefaultOptions: IProjectMember[] =
|
||||
defaultOptions.map((member: any) => ({ member: { ...member } })) ?? [];
|
||||
const projectMembers = _projectMembers ?? updatedDefaultOptions;
|
||||
|
||||
const options = projectMembers?.map((member) => ({
|
||||
value: member.member.id,
|
||||
query: member.member.display_name,
|
||||
content: (
|
||||
@ -82,7 +80,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
|
||||
// if multiple assignees
|
||||
if (Array.isArray(value)) {
|
||||
const assignees = workspaceMembers?.filter((m) => value.includes(m.member.id));
|
||||
const assignees = projectMembers?.filter((m) => value.includes(m.member.id));
|
||||
|
||||
if (!assignees || assignees.length === 0) return "No Assignee";
|
||||
|
||||
@ -93,7 +91,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
}
|
||||
|
||||
// if single assignee
|
||||
const assignee = workspaceMembers?.find((m) => m.member.id === value)?.member;
|
||||
const assignee = projectMembers?.find((m) => m.member.id === value)?.member;
|
||||
|
||||
if (!assignee) return "No Assignee";
|
||||
|
||||
@ -107,7 +105,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
{value && value.length > 0 && Array.isArray(value) ? (
|
||||
<AvatarGroup showTooltip={false}>
|
||||
{value.map((assigneeId) => {
|
||||
const member = workspaceMembers?.find((m) => m.member.id === assigneeId)?.member;
|
||||
const member = projectMembers?.find((m) => m.member.id === assigneeId)?.member;
|
||||
if (!member) return null;
|
||||
return <Avatar key={member.id} name={member.display_name} src={member.avatar} />;
|
||||
})}
|
||||
@ -149,7 +147,7 @@ export const IssuePropertyAssignee: React.FC<IIssuePropertyAssignee> = observer(
|
||||
className={`flex items-center justify-between gap-1 w-full text-xs ${
|
||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
onClick={() => !workspaceMembers && getWorkspaceMembers()}
|
||||
onClick={() => !projectMembers && getWorkspaceMembers()}
|
||||
>
|
||||
{label}
|
||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||
|
@ -10,10 +10,12 @@ import { Check, ChevronDown, Search } from "lucide-react";
|
||||
// types
|
||||
import { Placement } from "@popperjs/core";
|
||||
import { RootStore } from "store/root";
|
||||
import { IIssueLabel } from "types";
|
||||
|
||||
export interface IIssuePropertyLabels {
|
||||
projectId: string | null;
|
||||
value: string[];
|
||||
defaultOptions?: any;
|
||||
onChange: (data: string[]) => void;
|
||||
disabled?: boolean;
|
||||
hideDropdownArrow?: boolean;
|
||||
@ -29,6 +31,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
const {
|
||||
projectId,
|
||||
value,
|
||||
defaultOptions = [],
|
||||
onChange,
|
||||
disabled,
|
||||
hideDropdownArrow = false,
|
||||
@ -42,7 +45,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
|
||||
const {
|
||||
workspace: workspaceStore,
|
||||
projectLabel: { fetchProjectLabels, projectLabels },
|
||||
projectLabel: { fetchProjectLabels, labels },
|
||||
}: RootStore = useMobxStore();
|
||||
const workspaceSlug = workspaceStore?.workspaceSlug;
|
||||
|
||||
@ -59,7 +62,11 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
|
||||
if (!value) return null;
|
||||
|
||||
const options = (projectLabels ? projectLabels : []).map((label) => ({
|
||||
let projectLabels: IIssueLabel[] = defaultOptions;
|
||||
const storeLabels = projectId && labels ? labels[projectId] : [];
|
||||
if (storeLabels && storeLabels.length > 0) projectLabels = storeLabels;
|
||||
|
||||
const options = projectLabels.map((label) => ({
|
||||
value: label.id,
|
||||
query: label.name,
|
||||
content: (
|
||||
@ -95,7 +102,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
{value.length > 0 ? (
|
||||
value.length <= maxRender ? (
|
||||
<>
|
||||
{(projectLabels ? projectLabels : [])
|
||||
{projectLabels
|
||||
?.filter((l) => value.includes(l.id))
|
||||
.map((label) => (
|
||||
<Tooltip position="top" tooltipHeading="Labels" tooltipContent={label.name ?? ""}>
|
||||
@ -123,7 +130,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
<Tooltip
|
||||
position="top"
|
||||
tooltipHeading="Labels"
|
||||
tooltipContent={(projectLabels ? projectLabels : [])
|
||||
tooltipContent={projectLabels
|
||||
?.filter((l) => value.includes(l.id))
|
||||
.map((l) => l.name)
|
||||
.join(", ")}
|
||||
@ -167,7 +174,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
? "cursor-pointer"
|
||||
: "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
onClick={() => !projectLabels && fetchLabels()}
|
||||
onClick={() => !storeLabels && fetchLabels()}
|
||||
>
|
||||
{label}
|
||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||
@ -200,8 +207,9 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
className={({ selected }) =>
|
||||
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80
|
||||
${selected ? "text-custom-text-100" : "text-custom-text-200"}`
|
||||
`flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 hover:bg-custom-background-80 ${
|
||||
selected ? "text-custom-text-100" : "text-custom-text-200"
|
||||
}`
|
||||
}
|
||||
>
|
||||
{({ selected }) => (
|
||||
|
@ -17,6 +17,7 @@ import { RootStore } from "store/root";
|
||||
export interface IIssuePropertyState {
|
||||
projectId: string | null;
|
||||
value: any | string | null;
|
||||
defaultOptions?: any;
|
||||
onChange: (state: IState) => void;
|
||||
disabled?: boolean;
|
||||
hideDropdownArrow?: boolean;
|
||||
@ -30,6 +31,7 @@ export const IssuePropertyState: React.FC<IIssuePropertyState> = observer((props
|
||||
const {
|
||||
projectId,
|
||||
value,
|
||||
defaultOptions = [],
|
||||
onChange,
|
||||
disabled,
|
||||
hideDropdownArrow = false,
|
||||
@ -47,10 +49,9 @@ export const IssuePropertyState: React.FC<IIssuePropertyState> = observer((props
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<Boolean>(false);
|
||||
|
||||
const projectStates: IState[] = [];
|
||||
const projectStatesByGroup = projectStateStore.groupedProjectStates;
|
||||
if (projectStatesByGroup)
|
||||
for (const group in projectStatesByGroup) projectStates.push(...projectStatesByGroup[group]);
|
||||
let projectStates: IState[] = defaultOptions;
|
||||
const storeStates = projectId ? projectStateStore.states[projectId] : [];
|
||||
if (storeStates && storeStates.length > 0) projectStates = storeStates;
|
||||
|
||||
const fetchProjectStates = () => {
|
||||
setIsLoading(true);
|
||||
@ -120,7 +121,7 @@ export const IssuePropertyState: React.FC<IIssuePropertyState> = observer((props
|
||||
className={`flex items-center justify-between h-5 gap-1 w-full text-xs px-2.5 py-1 rounded border-[0.5px] border-custom-border-300 ${
|
||||
disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80"
|
||||
} ${buttonClassName}`}
|
||||
onClick={() => !projectStatesByGroup && fetchProjectStates()}
|
||||
onClick={() => !storeStates && fetchProjectStates()}
|
||||
>
|
||||
{label}
|
||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||
|
@ -25,6 +25,7 @@ export const SpreadsheetAssigneeColumn: React.FC<Props> = ({ issue, members, onC
|
||||
<IssuePropertyAssignee
|
||||
projectId={issue.project_detail?.id ?? null}
|
||||
value={issue.assignees}
|
||||
defaultOptions={issue?.assignee_details ? issue.assignee_details : []}
|
||||
onChange={(data) => onChange({ assignees: data })}
|
||||
className="h-full w-full"
|
||||
buttonClassName="!shadow-none !border-0 h-full w-full px-2.5 py-1 "
|
||||
|
@ -27,6 +27,7 @@ export const SpreadsheetLabelColumn: React.FC<Props> = (props) => {
|
||||
<IssuePropertyLabels
|
||||
projectId={issue.project_detail?.id ?? null}
|
||||
value={issue.labels}
|
||||
defaultOptions={issue?.label_details ? issue.label_details : []}
|
||||
onChange={(data) => onChange({ labels: data })}
|
||||
className="h-full w-full"
|
||||
buttonClassName="px-2.5 h-full"
|
||||
|
@ -27,6 +27,7 @@ export const SpreadsheetStateColumn: React.FC<Props> = (props) => {
|
||||
<IssuePropertyState
|
||||
projectId={issue.project_detail?.id ?? null}
|
||||
value={issue.state_detail.id}
|
||||
defaultOptions={issue?.state_detail ? [issue.state_detail] : []}
|
||||
onChange={(data) => onChange({ state: data.id, state_detail: data })}
|
||||
className="h-full w-full"
|
||||
buttonClassName="!shadow-none !border-0 h-full w-full"
|
||||
|
Loading…
Reference in New Issue
Block a user