fix: archived issues and minor bug fixes (#3451)

* make computedFn without optional arguments

* fix archived issues

* fix activity changes with proper context

* fix display filters that require server side filtering
This commit is contained in:
rahulramesha 2024-01-24 18:50:54 +05:30 committed by GitHub
parent 338d58f79d
commit 6a2be6afc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 118 additions and 39 deletions

View File

@ -94,7 +94,7 @@ const EstimatePoint = observer((props: { point: string }) => {
const { areEstimatesEnabledForCurrentProject, getEstimatePointValue } = useEstimate(); const { areEstimatesEnabledForCurrentProject, getEstimatePointValue } = useEstimate();
const currentPoint = Number(point) + 1; const currentPoint = Number(point) + 1;
const estimateValue = getEstimatePointValue(Number(point)); const estimateValue = getEstimatePointValue(Number(point), null);
return ( return (
<span className="font-medium text-custom-text-100"> <span className="font-medium text-custom-text-100">
@ -142,8 +142,18 @@ const activityDetails: {
}, },
archived_at: { archived_at: {
message: (activity) => { message: (activity) => {
if (activity.new_value === "restore") return "restored the issue."; if (activity.new_value === "restore")
else return "archived the issue."; return (
<>
restored <IssueLink activity={activity} />
</>
);
else
return (
<>
archived <IssueLink activity={activity} />
</>
);
}, },
icon: <ArchiveIcon size={12} color="#6b7280" aria-hidden="true" />, icon: <ArchiveIcon size={12} color="#6b7280" aria-hidden="true" />,
}, },
@ -229,8 +239,18 @@ const activityDetails: {
}, },
issue: { issue: {
message: (activity) => { message: (activity) => {
if (activity.verb === "created") return "created the issue."; if (activity.verb === "created")
else return "deleted an issue."; return (
<>
created <IssueLink activity={activity} />
</>
);
else
return (
<>
deleted <IssueLink activity={activity} />
</>
);
}, },
icon: <LayersIcon width={12} height={12} color="#6b7280" aria-hidden="true" />, icon: <LayersIcon width={12} height={12} color="#6b7280" aria-hidden="true" />,
}, },
@ -371,7 +391,7 @@ const activityDetails: {
else else
return ( return (
<> <>
removed the issue from the cycle{" "} removed <IssueLink activity={activity} /> from the cycle{" "}
<a <a
href={`/${workspaceSlug}/projects/${activity.project}/cycles/${activity.old_identifier}`} href={`/${workspaceSlug}/projects/${activity.project}/cycles/${activity.old_identifier}`}
target="_blank" target="_blank"
@ -418,7 +438,7 @@ const activityDetails: {
else else
return ( return (
<> <>
removed the issue from the module{" "} removed <IssueLink activity={activity} /> from the module{" "}
<a <a
href={`/${workspaceSlug}/projects/${activity.project}/modules/${activity.old_identifier}`} href={`/${workspaceSlug}/projects/${activity.project}/modules/${activity.old_identifier}`}
target="_blank" target="_blank"

View File

@ -20,7 +20,7 @@ export const IssueEstimateActivity: FC<TIssueEstimateActivity> = observer((props
if (!activity) return <></>; if (!activity) return <></>;
const estimateValue = getEstimatePointValue(Number(activity.new_value)); const estimateValue = getEstimatePointValue(Number(activity.new_value), null);
const currentPoint = Number(activity.new_value) + 1; const currentPoint = Number(activity.new_value) + 1;
return ( return (

View File

@ -18,12 +18,11 @@ type Props = {
projectId: string; projectId: string;
issueId: string; issueId: string;
issueOperations: TIssueOperations; issueOperations: TIssueOperations;
is_archived: boolean;
is_editable: boolean; is_editable: boolean;
}; };
export const IssueMainContent: React.FC<Props> = observer((props) => { export const IssueMainContent: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, issueId, issueOperations, is_archived, is_editable } = props; const { workspaceSlug, projectId, issueId, issueOperations, is_editable } = props;
// states // states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
// hooks // hooks

View File

@ -217,8 +217,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = (props) => {
projectId={projectId} projectId={projectId}
issueId={issueId} issueId={issueId}
issueOperations={issueOperations} issueOperations={issueOperations}
is_archived={is_archived} is_editable={!is_archived && is_editable}
is_editable={is_editable}
/> />
</div> </div>
<div className="h-full w-1/3 space-y-5 overflow-hidden border-l border-custom-border-300 py-5"> <div className="h-full w-1/3 space-y-5 overflow-hidden border-l border-custom-border-300 py-5">
@ -228,7 +227,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = (props) => {
issueId={issueId} issueId={issueId}
issueOperations={issueOperations} issueOperations={issueOperations}
is_archived={is_archived} is_archived={is_archived}
is_editable={is_editable} is_editable={!is_archived && is_editable}
/> />
</div> </div>
</div> </div>

View File

@ -28,6 +28,8 @@ export const ArchivedIssueListLayout: FC = observer(() => {
[issues, workspaceSlug, projectId] [issues, workspaceSlug, projectId]
); );
const canEditPropertiesBasedOnProject = () => false;
return ( return (
<BaseListRoot <BaseListRoot
issuesFilter={issuesFilter} issuesFilter={issuesFilter}
@ -35,6 +37,7 @@ export const ArchivedIssueListLayout: FC = observer(() => {
QuickActions={ArchivedIssueQuickActions} QuickActions={ArchivedIssueQuickActions}
issueActions={issueActions} issueActions={issueActions}
storeType={EIssuesStoreType.PROJECT} storeType={EIssuesStoreType.PROJECT}
canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject}
/> />
); );
}); });

View File

@ -53,13 +53,16 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
} }
); );
const canEditProperties = (projectId: string | undefined) => { const canEditProperties = useCallback(
(projectId: string | undefined) => {
if (!projectId) return false; if (!projectId) return false;
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId]; const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER; return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
}; },
[currentWorkspaceAllProjectsRole]
);
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined; const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;

View File

@ -5,7 +5,12 @@ import useSWR from "swr";
// mobx store // mobx store
import { useIssues } from "hooks/store"; import { useIssues } from "hooks/store";
// components // components
import { ArchivedIssueListLayout, ArchivedIssueAppliedFiltersRoot, ProjectEmptyState } from "components/issues"; import {
ArchivedIssueListLayout,
ArchivedIssueAppliedFiltersRoot,
ProjectEmptyState,
IssuePeekOverview,
} from "components/issues";
import { EIssuesStoreType } from "constants/issue"; import { EIssuesStoreType } from "constants/issue";
// ui // ui
import { Spinner } from "@plane/ui"; import { Spinner } from "@plane/ui";
@ -46,9 +51,14 @@ export const ArchivedIssueLayoutRoot: React.FC = observer(() => {
// TODO: Replace this with project view empty state // TODO: Replace this with project view empty state
<ProjectEmptyState /> <ProjectEmptyState />
) : ( ) : (
<>
<div className="relative h-full w-full overflow-auto"> <div className="relative h-full w-full overflow-auto">
<ArchivedIssueListLayout /> <ArchivedIssueListLayout />
</div> </div>
{/* peek overview */}
<IssuePeekOverview is_archived />
</>
)} )}
</> </>
)} )}

View File

@ -10,7 +10,6 @@ interface IPeekOverviewIssueDetails {
projectId: string; projectId: string;
issueId: string; issueId: string;
issueOperations: TIssueOperations; issueOperations: TIssueOperations;
is_archived: boolean;
disabled: boolean; disabled: boolean;
isSubmitting: "submitting" | "submitted" | "saved"; isSubmitting: "submitting" | "submitted" | "saved";
setIsSubmitting: (value: "submitting" | "submitted" | "saved") => void; setIsSubmitting: (value: "submitting" | "submitted" | "saved") => void;

View File

@ -208,7 +208,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
issueId={peekIssue.issueId} issueId={peekIssue.issueId}
isLoading={isLoading} isLoading={isLoading}
is_archived={is_archived} is_archived={is_archived}
disabled={!is_editable} disabled={is_archived || !is_editable}
issueOperations={issueOperations} issueOperations={issueOperations}
/> />
</Fragment> </Fragment>

View File

@ -64,14 +64,13 @@ export const IssueView: FC<IIssueView> = observer((props) => {
// ref // ref
const issuePeekOverviewRef = useRef<HTMLDivElement>(null); const issuePeekOverviewRef = useRef<HTMLDivElement>(null);
// store hooks // store hooks
const { activity, setPeekIssue, isAnyModalOpen, isDeleteIssueModalOpen, toggleDeleteIssueModal } = useIssueDetail(); const { setPeekIssue, isAnyModalOpen, isDeleteIssueModalOpen, toggleDeleteIssueModal } = useIssueDetail();
const { currentUser } = useUser(); const { currentUser } = useUser();
const { const {
issue: { getIssueById }, issue: { getIssueById },
} = useIssueDetail(); } = useIssueDetail();
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// derived values // derived values
const issueActivity = activity.getActivitiesByIssueId(issueId);
const currentMode = PEEK_OPTIONS.find((m) => m.key === peekMode); const currentMode = PEEK_OPTIONS.find((m) => m.key === peekMode);
const issue = getIssueById(issueId); const issue = getIssueById(issueId);
@ -213,15 +212,11 @@ export const IssueView: FC<IIssueView> = observer((props) => {
<> <>
{["side-peek", "modal"].includes(peekMode) ? ( {["side-peek", "modal"].includes(peekMode) ? (
<div className="relative flex flex-col gap-3 px-8 py-5"> <div className="relative flex flex-col gap-3 px-8 py-5">
{is_archived && (
<div className="absolute left-0 top-0 z-[9] flex h-full min-h-full w-full items-center justify-center bg-custom-background-100 opacity-60" />
)}
<PeekOverviewIssueDetails <PeekOverviewIssueDetails
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
issueId={issueId} issueId={issueId}
issueOperations={issueOperations} issueOperations={issueOperations}
is_archived={is_archived}
disabled={disabled} disabled={disabled}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
setIsSubmitting={(value) => setIsSubmitting(value)} setIsSubmitting={(value) => setIsSubmitting(value)}
@ -243,15 +238,14 @@ export const IssueView: FC<IIssueView> = observer((props) => {
/> />
</div> </div>
) : ( ) : (
<div className={`flex h-full w-full overflow-auto ${is_archived ? "opacity-60" : ""}`}> <div className={`flex h-full w-full overflow-auto`}>
<div className="relative h-full w-full space-y-6 overflow-auto p-4 py-5"> <div className="relative h-full w-full space-y-6 overflow-auto p-4 py-5">
<div className={is_archived ? "pointer-events-none" : ""}> <div>
<PeekOverviewIssueDetails <PeekOverviewIssueDetails
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
issueId={issueId} issueId={issueId}
issueOperations={issueOperations} issueOperations={issueOperations}
is_archived={is_archived}
disabled={disabled} disabled={disabled}
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
setIsSubmitting={(value) => setIsSubmitting(value)} setIsSubmitting={(value) => setIsSubmitting(value)}

View File

@ -59,6 +59,7 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => {
workspaceSlug={workspaceSlug.toString()} workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()} projectId={projectId.toString()}
issueId={issueId.toString()} issueId={issueId.toString()}
is_archived={!!issue?.archived_at}
/> />
) )
)} )}

View File

@ -18,7 +18,7 @@ export interface IEstimateStore {
activeEstimateDetails: IEstimate | null; activeEstimateDetails: IEstimate | null;
// computed actions // computed actions
areEstimatesEnabledForProject: (projectId: string) => boolean; areEstimatesEnabledForProject: (projectId: string) => boolean;
getEstimatePointValue: (estimateKey: number | null, projectId?: string) => string; getEstimatePointValue: (estimateKey: number | null, projectId: string | null) => string;
getProjectEstimateById: (estimateId: string) => IEstimate | null; getProjectEstimateById: (estimateId: string) => IEstimate | null;
getProjectActiveEstimateDetails: (projectId: string) => IEstimate | null; getProjectActiveEstimateDetails: (projectId: string) => IEstimate | null;
// fetch actions // fetch actions
@ -109,7 +109,7 @@ export class EstimateStore implements IEstimateStore {
/** /**
* @description returns the point value for the given estimate key to display in the UI * @description returns the point value for the given estimate key to display in the UI
*/ */
getEstimatePointValue = computedFn((estimateKey: number | null, projectId?: string) => { getEstimatePointValue = computedFn((estimateKey: number | null, projectId: string | null) => {
if (estimateKey === null) return "None"; if (estimateKey === null) return "None";
const activeEstimate = projectId ? this.getProjectActiveEstimateDetails(projectId) : this.activeEstimateDetails; const activeEstimate = projectId ? this.getProjectActiveEstimateDetails(projectId) : this.activeEstimateDetails;
return activeEstimate?.points?.find((point) => point.key === estimateKey)?.value || "None"; return activeEstimate?.points?.find((point) => point.key === estimateKey)?.value || "None";

View File

@ -194,6 +194,9 @@ export class ArchivedIssuesFilter extends IssueFilterHelperStore implements IArc
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.archivedIssues.fetchIssues(workspaceSlug, projectId, "mutation");
this.handleIssuesLocalFilters.set(EIssuesStoreType.ARCHIVED, type, workspaceSlug, projectId, undefined, { this.handleIssuesLocalFilters.set(EIssuesStoreType.ARCHIVED, type, workspaceSlug, projectId, undefined, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -90,11 +90,15 @@ export class ArchivedIssues extends IssueHelperStore implements IArchivedIssues
const response = await this.archivedIssueService.getArchivedIssues(workspaceSlug, projectId, params); const response = await this.archivedIssueService.getArchivedIssues(workspaceSlug, projectId, params);
runInAction(() => { runInAction(() => {
set(this.issues, [projectId], Object.keys(response)); set(
this.issues,
[projectId],
response.map((issue: TIssue) => issue.id)
);
this.loader = undefined; this.loader = undefined;
}); });
this.rootIssueStore.issues.addIssue(Object.values(response)); this.rootIssueStore.issues.addIssue(response);
return response; return response;
} catch (error) { } catch (error) {

View File

@ -205,6 +205,9 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.cycleIssues.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
await this.issueFilterService.patchCycleIssueFilters(workspaceSlug, projectId, cycleId, { await this.issueFilterService.patchCycleIssueFilters(workspaceSlug, projectId, cycleId, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });
@ -259,3 +262,5 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
} }
}; };
} }

View File

@ -189,6 +189,9 @@ export class DraftIssuesFilter extends IssueFilterHelperStore implements IDraftI
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.draftIssues.fetchIssues(workspaceSlug, projectId, "mutation");
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, { this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -183,6 +183,20 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
updated_on: displayProperties?.updated_on ?? true, updated_on: displayProperties?.updated_on ?? true,
}); });
/**
* This Method returns true if the display properties changed requires a server side update
* @param displayFilters
* @returns
*/
requiresServerUpdate = (displayFilters: IIssueDisplayFilterOptions) => {
const SERVER_DISPLAY_FILTERS = ["sub_issue", "type"];
const displayFilterKeys = Object.keys(displayFilters);
return SERVER_DISPLAY_FILTERS.some((serverDisplayfilter: string) =>
displayFilterKeys.includes(serverDisplayfilter)
);
};
handleIssuesLocalFilters = { handleIssuesLocalFilters = {
fetchFiltersFromStorage: () => { fetchFiltersFromStorage: () => {
const _filters = storage.get("issue_local_filters"); const _filters = storage.get("issue_local_filters");

View File

@ -9,7 +9,7 @@ export interface IIssueKanBanViewStore {
subgroupByIssuesVisibility: string[]; subgroupByIssuesVisibility: string[];
}; };
// computed // computed
getCanUserDragDrop: (order_by: string | null, group_by: string | null, sub_group_by?: string | null) => boolean; getCanUserDragDrop: (group_by: string | null, sub_group_by: string | null) => boolean;
canUserDragDropVertically: boolean; canUserDragDropVertically: boolean;
canUserDragDropHorizontally: boolean; canUserDragDropHorizontally: boolean;
// actions // actions
@ -38,7 +38,7 @@ export class IssueKanBanViewStore implements IIssueKanBanViewStore {
this.rootStore = _rootStore; this.rootStore = _rootStore;
} }
getCanUserDragDrop = computedFn((group_by: string | null, sub_group_by?: string | null) => { getCanUserDragDrop = computedFn((group_by: string | null, sub_group_by: string | null) => {
if (group_by && ["state", "priority"].includes(group_by)) { if (group_by && ["state", "priority"].includes(group_by)) {
if (!sub_group_by) return true; if (!sub_group_by) return true;
if (sub_group_by && ["state", "priority"].includes(sub_group_by)) return true; if (sub_group_by && ["state", "priority"].includes(sub_group_by)) return true;

View File

@ -204,6 +204,9 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.moduleIssues.fetchIssues(workspaceSlug, projectId, "mutation", moduleId);
await this.issueFilterService.patchModuleIssueFilters(workspaceSlug, projectId, moduleId, { await this.issueFilterService.patchModuleIssueFilters(workspaceSlug, projectId, moduleId, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -199,6 +199,15 @@ export class ProfileIssuesFilter extends IssueFilterHelperStore implements IProf
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.profileIssues.fetchIssues(
workspaceSlug,
undefined,
"mutation",
userId,
this.rootIssueStore.profileIssues.currentView
);
this.handleIssuesLocalFilters.set(EIssuesStoreType.PROFILE, type, workspaceSlug, userId, undefined, { this.handleIssuesLocalFilters.set(EIssuesStoreType.PROFILE, type, workspaceSlug, userId, undefined, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -203,6 +203,9 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.projectViewIssues.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
await this.issueFilterService.patchView(workspaceSlug, projectId, viewId, { await this.issueFilterService.patchView(workspaceSlug, projectId, viewId, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -201,6 +201,9 @@ export class ProjectIssuesFilter extends IssueFilterHelperStore implements IProj
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.projectIssues.fetchIssues(workspaceSlug, projectId, "mutation");
await this.issueFilterService.patchProjectIssueFilters(workspaceSlug, projectId, { await this.issueFilterService.patchProjectIssueFilters(workspaceSlug, projectId, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,
}); });

View File

@ -222,6 +222,10 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
); );
}); });
}); });
if (this.requiresServerUpdate(updatedDisplayFilters))
this.rootIssueStore.workspaceIssues.fetchIssues(workspaceSlug, viewId, "mutation");
if (["all-issues", "assigned", "created", "subscribed"].includes(viewId)) if (["all-issues", "assigned", "created", "subscribed"].includes(viewId))
this.handleIssuesLocalFilters.set(EIssuesStoreType.GLOBAL, type, workspaceSlug, undefined, viewId, { this.handleIssuesLocalFilters.set(EIssuesStoreType.GLOBAL, type, workspaceSlug, undefined, viewId, {
display_filters: _filters.displayFilters, display_filters: _filters.displayFilters,