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:
guru_sainath 2023-12-01 17:35:33 +05:30 committed by Aaryan Khandelwal
parent 0ff5f9ef62
commit ef14f00777
8 changed files with 44 additions and 28 deletions

View File

@ -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}

View File

@ -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}

View File

@ -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" />}

View File

@ -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 }) => (

View File

@ -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" />}

View File

@ -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 "

View File

@ -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"

View File

@ -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"