[WEB-2232] fix: project grouping UI changes (#856)

* fix: project grouping bugs

* feat: join btn change + access filters

* fix: handled states api call + made attributes common

* fix: archived projects handling

* fix: removed log

* fix: copy changes

* fix: UI based changes
This commit is contained in:
Akshita Goyal
2024-08-16 21:03:16 +05:30
committed by GitHub
parent db15aed3c2
commit 3c91c2fc18
10 changed files with 69 additions and 25 deletions

View File

@@ -15,7 +15,7 @@ import { getRandomEmoji } from "@/helpers/emoji.helper";
// hooks
import { useEventTracker, useMember, useProject, useUser, useWorkspace } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useWorkspaceFeatures } from "@/plane-web/hooks/store";
import { E_FEATURE_FLAGS, useFlag, useWorkspaceFeatures } from "@/plane-web/hooks/store";
import { TProject } from "@/plane-web/types/projects";
import { EWorkspaceFeatures } from "@/plane-web/types/workspace-feature";
import ProjectAttributes from "./attributes";
@@ -58,7 +58,9 @@ export const CreateProjectForm: FC<Props> = observer((props) => {
const { data: currentUser } = useUser();
const { isWorkspaceFeatureEnabled } = useWorkspaceFeatures();
const isProjectGroupingEnabled = isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED);
const isProjectGroupingEnabled =
isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED) &&
useFlag(workspaceSlug.toString(), E_FEATURE_FLAGS.PROJECT_GROUPING);
// form info
const methods = useForm<TProject>({

View File

@@ -10,7 +10,7 @@ import { PROJECT_SCOPES } from "@/plane-web/constants/project";
// plane web hooks
import { useProjectFilter } from "@/plane-web/hooks/store";
// plane web types
import { EProjectScope } from "@/plane-web/types/workspace-project-filters";
import { EProjectLayouts, EProjectScope } from "@/plane-web/types/workspace-project-filters";
export type TProjectScopeDropdown = {
workspaceSlug: string;
@@ -20,7 +20,7 @@ export type TProjectScopeDropdown = {
export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props) => {
const { workspaceSlug, className } = props;
// hooks
const { scopeProjectsCount, filters, updateScope } = useProjectFilter();
const { scopeProjectsCount, filters, updateScope, updateLayout } = useProjectFilter();
// derived values
const selectedScope = filters?.scope || EProjectScope.ALL_PROJECTS;
@@ -28,13 +28,17 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
const DropdownLabel = () => (
<>
<div className="hidden md:flex relative items-center gap-2">
<div className="whitespace-nowrap font-medium">
{(PROJECT_SCOPES || []).find((scope) => selectedScope === scope.key)?.label}
</div>
<div className="px-2 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">
{selectedScopeCount}
<div className="hidden md:flex relative items-center gap-2 w-[160px]">
<div className="flex gap-2 flex-1 my-auto">
{" "}
<div className="whitespace-nowrap font-medium my-auto">
{(PROJECT_SCOPES || []).find((scope) => selectedScope === scope.key)?.label}
</div>
<div className="px-2 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">
{selectedScopeCount}
</div>
</div>
<ChevronDown className="h-3 w-3" strokeWidth={2} />
</div>
<div className="flex md:hidden text-sm items-center gap-2 neutral-primary text-custom-text-200 justify-center w-full">
<span>{(PROJECT_SCOPES || []).find((scope) => selectedScope === scope.key)?.label}</span>
@@ -48,7 +52,15 @@ export const ProjectScopeDropdown: FC<TProjectScopeDropdown> = observer((props)
<CustomMenu.MenuItem
key={scope.key}
className="flex items-center gap-2 truncate"
onClick={() => updateScope(workspaceSlug, scope.key)}
onClick={() => {
updateScope(workspaceSlug, scope.key);
if (
scope.key === EProjectScope.ALL_PROJECTS &&
filters?.layout &&
![EProjectLayouts.GALLERY, EProjectLayouts.TABLE].includes(filters?.layout)
)
updateLayout(workspaceSlug, EProjectLayouts.GALLERY);
}}
>
<div className="truncate font-medium text-xs">{scope?.label}</div>
<div className="px-2 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">

View File

@@ -10,7 +10,7 @@ import { PROJECT_LAYOUTS } from "@/plane-web/constants/project";
// plane web hooks
import { useProjectFilter } from "@/plane-web/hooks/store";
// plane web types
import { EProjectLayouts } from "@/plane-web/types/workspace-project-filters";
import { EProjectLayouts, EProjectScope } from "@/plane-web/types/workspace-project-filters";
type TProjectLayoutSelection = {
workspaceSlug: string;
@@ -48,7 +48,9 @@ export const ProjectLayoutSelection: FC<TProjectLayoutSelection> = observer((pro
))}
</CustomMenu>
<div className="hidden md:flex items-center gap-1 rounded bg-custom-background-80 p-1">
{PROJECT_LAYOUTS.map((layout) => (
{PROJECT_LAYOUTS.filter(
(layout) => !layout.selectivelyHide || filters?.scope === EProjectScope.MY_PROJECTS
).map((layout) => (
<Tooltip key={layout.key} tooltipContent={layout.title} isMobile={isMobile}>
<button
type="button"

View File

@@ -18,7 +18,7 @@ import {
ProjectScopeDropdown,
ProjectSearch,
} from "@/plane-web/components/projects/";
import { useWorkspaceFeatures } from "@/plane-web/hooks/store";
import { useFlag, useWorkspaceFeatures, E_FEATURE_FLAGS } from "@/plane-web/hooks/store";
import { EWorkspaceFeatures } from "@/plane-web/types/workspace-feature";
export const ProjectsListHeader = observer(() => {
@@ -30,7 +30,10 @@ export const ProjectsListHeader = observer(() => {
// derived values
const workspaceId = currentWorkspace?.id || undefined;
const isProjectGroupingEnabled = isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED);
const isProjectGroupingEnabled =
isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED) &&
useFlag(workspaceSlug.toString(), E_FEATURE_FLAGS.PROJECT_GROUPING);
const isArchived = pathname.includes("/archives");
if (!workspaceSlug || !workspaceId) return <></>;

View File

@@ -9,7 +9,9 @@ import { ArchiveRestoreProjectModal, DeleteProjectModal, JoinProjectModal } from
// hooks
import { useProject, useWorkspace } from "@/hooks/store";
// types
import { useProjectFilter } from "@/plane-web/hooks/store";
import { TProject } from "@/plane-web/types/projects";
import { EProjectScope } from "@/plane-web/types/workspace-project-filters";
import JoinButton from "../../common/join-button";
import Attributes from "../attributes";
import Details from "./details";
@@ -29,6 +31,7 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
const pathname = usePathname();
const router = useRouter();
const { updateProject } = useProject();
const { filters } = useProjectFilter();
const isArchived = pathname.includes("/archives");
const handleUpdateProject = (data: Partial<TProject>) => {
@@ -108,7 +111,7 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
</>
</Link>
{!project.archived_at && <JoinButton project={project} />}
{!project.archived_at && filters?.scope === EProjectScope.ALL_PROJECTS && <JoinButton project={project} />}
</div>
);
});

View File

@@ -11,8 +11,9 @@ import { IGanttBlock } from "@/components/gantt-chart";
import { findTotalDaysInRange, renderFormattedDate } from "@/helpers/date-time.helper";
import { useProject } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useWorkspaceProjectStates } from "@/plane-web/hooks/store";
import { useProjectFilter, useWorkspaceProjectStates } from "@/plane-web/hooks/store";
import { TProject } from "@/plane-web/types/projects";
import { EProjectScope } from "@/plane-web/types/workspace-project-filters";
import JoinButton from "../../common/join-button";
type Props = {
@@ -68,6 +69,7 @@ export const ProjectGanttBlock: React.FC<Props> = observer((props) => {
// rendering projects on gantt sidebar
export const ProjectGanttSidebarBlock: React.FC<SidebarProps> = observer((props) => {
const { block } = props;
const { filters } = useProjectFilter();
// router
const { workspaceSlug: routerWorkspaceSlug } = useParams();
@@ -89,7 +91,7 @@ export const ProjectGanttSidebarBlock: React.FC<SidebarProps> = observer((props)
<span className="flex-grow text-sm font-medium max-w-[150px] truncate">{projectDetails?.name}</span>
</Tooltip>
</div>
<JoinButton project={projectDetails} />
{filters?.scope === EProjectScope.ALL_PROJECTS && <JoinButton project={projectDetails} />}
<div className="flex-shrink-0 h-full text-sm font-medium py-3 ml-2">
{duration} day{duration > 1 ? "s" : ""}
</div>

View File

@@ -11,7 +11,9 @@ import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useProject, useWorkspace } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
import { useProjectFilter } from "@/plane-web/hooks/store";
import { TProject } from "@/plane-web/types/projects";
import { EProjectScope } from "@/plane-web/types/workspace-project-filters";
import JoinButton from "../../common/join-button";
import QuickActions from "../../quick-actions";
import Attributes from "../attributes";
@@ -35,7 +37,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
const { getProjectById, updateProject } = useProject();
const { currentWorkspace } = useWorkspace();
const { filters } = useProjectFilter();
const { isMobile } = usePlatformOS();
const projectDetails = getProjectById(projectId);
@@ -112,7 +114,7 @@ export const ProjectBlock = observer((props: ProjectBlockProps) => {
handleUpdateProject={(data) => updateProject(workspaceSlug.toString(), projectDetails.id, data)}
workspaceSlug={workspaceSlug.toString()}
currentWorkspace={currentWorkspace}
cta={<JoinButton project={projectDetails as TProject} />}
cta={filters?.scope === EProjectScope.ALL_PROJECTS && <JoinButton project={projectDetails as TProject} />}
/>
<div
className={cn("hidden", {

View File

@@ -11,6 +11,7 @@ import { useWorkspace } from "@/hooks/store";
// plane web components
import { WorkspaceProjectsRoot } from "@/plane-web/components/projects";
import { useProjectFilter, useWorkspaceFeatures } from "@/plane-web/hooks/store";
import { E_FEATURE_FLAGS, useFlag } from "@/plane-web/hooks/store/use-flag";
import { EWorkspaceFeatures } from "@/plane-web/types/workspace-feature";
import { EProjectLayouts } from "@/plane-web/types/workspace-project-filters";
@@ -25,7 +26,9 @@ export const ProjectPageRoot = observer(() => {
// derived values
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Projects` : undefined;
const currentWorkspaceId = currentWorkspace?.id;
const isProjectGroupingEnabled = isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED);
const isProjectGroupingEnabled =
isWorkspaceFeatureEnabled(EWorkspaceFeatures.IS_PROJECT_GROUPING_ENABLED) &&
useFlag(workspaceSlug.toString(), E_FEATURE_FLAGS.PROJECT_GROUPING);
useEffect(() => {
if (pathname.includes("/archives")) {

View File

@@ -27,7 +27,7 @@ export const PROJECT_SCOPE_MAP: TProjectScopeMap = {
export const PROJECT_SCOPES = Object.values(PROJECT_SCOPE_MAP);
// layout constants
type TProjectLayoutMapObject<T> = { key: T; title: string; label: string; icon: LucideIcon };
type TProjectLayoutMapObject<T> = { key: T; title: string; label: string; icon: LucideIcon; selectivelyHide: boolean };
type TProjectLayoutMap = {
[key in EProjectLayouts]: TProjectLayoutMapObject<key>;
};
@@ -37,14 +37,28 @@ export const PROJECT_LAYOUT_MAP: TProjectLayoutMap = {
title: "Gallery Layout",
label: "Gallery",
icon: GalleryVertical,
selectivelyHide: false,
},
[EProjectLayouts.BOARD]: {
key: EProjectLayouts.BOARD,
title: "Board Layout",
label: "Board",
icon: Kanban,
selectivelyHide: true,
},
[EProjectLayouts.TABLE]: {
key: EProjectLayouts.TABLE,
title: "Table Layout",
label: "Table",
icon: List,
selectivelyHide: false,
},
[EProjectLayouts.BOARD]: { key: EProjectLayouts.BOARD, title: "Board Layout", label: "Board", icon: Kanban },
[EProjectLayouts.TABLE]: { key: EProjectLayouts.TABLE, title: "Table Layout", label: "Table", icon: List },
[EProjectLayouts.TIMELINE]: {
key: EProjectLayouts.TIMELINE,
title: "Timeline Layout",
label: "Timeline",
icon: GanttChartSquare,
selectivelyHide: true,
},
};
export const PROJECT_LAYOUTS = Object.values(PROJECT_LAYOUT_MAP);

View File

@@ -207,10 +207,11 @@ export class ProjectFilterStore extends ProjectFilterHelper implements IProjectF
if (!this.scopeMap[workspaceSlug]) {
this.updateScope(workspaceSlug, EProjectScope.ALL_PROJECTS);
}
const savedLayout = this.handleProjectLocalFilters.get(workspaceSlug)?.layout;
if (!this.layoutMap[workspaceSlug]) {
this.updateLayout(
workspaceSlug,
this.handleProjectLocalFilters.get(workspaceSlug)?.layout || EProjectLayouts.GALLERY
[EProjectLayouts.GALLERY, EProjectLayouts.TABLE].includes(savedLayout) ? savedLayout : EProjectLayouts.GALLERY
);
}
if (!this.attributesMap[workspaceSlug]) {