Merge pull request #771 from makeplane/sync/ce-ee

sync: community changes
This commit is contained in:
pushya22
2024-08-07 15:33:12 +05:30
committed by GitHub
11 changed files with 134 additions and 79 deletions

View File

@@ -27,14 +27,11 @@ class ProjectStatesEndpoint(BaseAPIView):
status=status.HTTP_404_NOT_FOUND,
)
states = (
State.objects.filter(
~Q(name="Triage"),
workspace__slug=deploy_board.workspace.slug,
project_id=deploy_board.project_id,
)
.values("name", "group", "color", "id")
)
states = State.objects.filter(
~Q(name="Triage"),
workspace__slug=deploy_board.workspace.slug,
project_id=deploy_board.project_id,
).values("name", "group", "color", "id", "sequence")
return Response(
states,

View File

@@ -1,9 +1,22 @@
type TLogoProps = {
in_use: "emoji" | "icon";
emoji?: {
value?: string;
url?: string;
};
icon?: {
name?: string;
color?: string;
};
};
export type IFavorite = {
id: string;
name: string;
entity_type: string;
entity_data: {
name: string;
logo_props?: TLogoProps | undefined;
};
is_folder: boolean;
sort_order: number;

View File

@@ -109,10 +109,10 @@ const getModuleColumns = (moduleStore: IIssueModuleStore): IGroupByColumn[] | un
};
const getStateColumns = (projectState: IStateStore): IGroupByColumn[] | undefined => {
const { states } = projectState;
if (!states) return;
const { sortedStates } = projectState;
if (!sortedStates) return;
return states.map((state) => ({
return sortedStates.map((state) => ({
id: state.id,
name: state.name,
icon: (

View File

@@ -1,11 +1,15 @@
import { action, makeObservable, observable, runInAction } from "mobx";
import clone from "lodash/clone";
import { action, computed, makeObservable, observable, runInAction } from "mobx";
import { IState } from "@plane/types";
import { sortStates } from "@/helpers/state.helper";
import { StateService } from "@/services/state.service";
import { CoreRootStore } from "./root.store";
export interface IStateStore {
// observables
states: IState[] | undefined;
//computed
sortedStates: IState[] | undefined;
// computed actions
getStateById: (stateId: string | undefined) => IState | undefined;
// fetch actions
@@ -21,6 +25,8 @@ export class StateStore implements IStateStore {
makeObservable(this, {
// observables
states: observable,
// computed
sortedStates: computed,
// fetch action
fetchStates: action,
});
@@ -28,6 +34,11 @@ export class StateStore implements IStateStore {
this.rootStore = _rootStore;
}
get sortedStates() {
if (!this.states) return;
return sortStates(clone(this.states));
}
getStateById = (stateId: string | undefined) => this.states?.find((state) => state.id === stateId);
fetchStates = async (anchor: string) => {

View File

@@ -0,0 +1,13 @@
import { IState } from "@plane/types";
import { STATE_GROUPS } from "@/constants/state";
export const sortStates = (states: IState[]) => {
if (!states || states.length === 0) return;
return states.sort((stateA, stateB) => {
if (stateA.group === stateB.group) {
return stateA.sequence - stateB.sequence;
}
return Object.keys(STATE_GROUPS).indexOf(stateA.group) - Object.keys(STATE_GROUPS).indexOf(stateB.group);
});
};

View File

@@ -294,7 +294,7 @@ export const CreateProjectForm: FC<Props> = observer((props) => {
/>
<Tooltip
isMobile={isMobile}
tooltipContent="Helps you identify issues in the project uniquely, (e.g. APP-123). Max 5 characters."
tooltipContent="Helps you identify issues in the project uniquely. Max 5 characters."
className="text-sm"
position="right-top"
>

View File

@@ -298,7 +298,7 @@ export const ProjectDetailsForm: FC<IProjectDetailsForm> = (props) => {
/>
<Tooltip
isMobile={isMobile}
tooltipContent="Helps you identify issues in the project uniquely, (e.g. APP-123). Max 5 characters."
tooltipContent="Helps you identify issues in the project uniquely. Max 5 characters."
className="text-sm"
position="right-top"
>

View File

@@ -9,7 +9,16 @@ import { useParams } from "next/navigation";
import { Briefcase, FileText, Layers, MoreHorizontal, Star } from "lucide-react";
// ui
import { IFavorite } from "@plane/types";
import { ContrastIcon, CustomMenu, DiceIcon, DragHandle, FavoriteFolderIcon, LayersIcon, Tooltip } from "@plane/ui";
import {
ContrastIcon,
CustomMenu,
DiceIcon,
DragHandle,
FavoriteFolderIcon,
LayersIcon,
Logo,
Tooltip,
} from "@plane/ui";
// components
import { SidebarNavItem } from "@/components/sidebar";
@@ -20,6 +29,17 @@ import { useAppTheme } from "@/hooks/store";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
import { usePlatformOS } from "@/hooks/use-platform-os";
const iconClassName = `flex-shrink-0 size-4 stroke-[1.5] m-auto`;
const ICONS: Record<string, JSX.Element> = {
page: <FileText className={iconClassName} />,
project: <Briefcase className={iconClassName} />,
view: <Layers className={iconClassName} />,
module: <DiceIcon className={iconClassName} />,
cycle: <ContrastIcon className={iconClassName} />,
issue: <LayersIcon className={iconClassName} />,
folder: <FavoriteFolderIcon className={iconClassName} />,
};
export const FavoriteItem = observer(
({
favoriteMap,
@@ -48,28 +68,18 @@ export const FavoriteItem = observer(
const dragHandleRef = useRef<HTMLButtonElement | null>(null);
const actionSectionRef = useRef<HTMLDivElement | null>(null);
const getIcon = () => {
const className = `flex-shrink-0 size-4 stroke-[1.5] m-auto`;
switch (favorite.entity_type) {
case "page":
return <FileText className={className} />;
case "project":
return <Briefcase className={className} />;
case "view":
return <Layers className={className} />;
case "module":
return <DiceIcon className={className} />;
case "cycle":
return <ContrastIcon className={className} />;
case "issue":
return <LayersIcon className={className} />;
case "folder":
return <FavoriteFolderIcon className={className} />;
default:
return <FileText />;
}
};
const getIcon = () => (
<>
<div className="hidden group-hover:block">{ICONS[favorite.entity_type] || <FileText />}</div>
<div className="block group-hover:hidden">
{favorite.entity_data?.logo_props?.in_use ? (
<Logo logo={favorite.entity_data?.logo_props} size={16} type="lucide" />
) : (
ICONS[favorite.entity_type] || <FileText />
)}
</div>
</>
);
const getLink = () => {
switch (favorite.entity_type) {
@@ -133,7 +143,10 @@ export const FavoriteItem = observer(
<div ref={elementRef} className="group/project-item">
<SidebarNavItem
key={favorite.id}
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
className={cn({
"bg-custom-sidebar-background-90": isMenuActive,
"p-0 size-8 aspect-square justify-center mx-auto": sidebarCollapsed,
})}
>
<div
className={cn("flex justify-between items-center gap-1.5 py-[1px]", {

View File

@@ -133,14 +133,16 @@ export const SidebarFavoritesMenu = observer(() => {
<span onClick={() => toggleFavoriteMenu(!isFavoriteMenuOpen)} className="flex-1 text-start">
YOUR FAVORITES
</span>
<span className="flex gap-2 flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded p-0.5 ">
<FolderPlus
onClick={() => {
setCreateNewFolder(true);
!isFavoriteMenuOpen && toggleFavoriteMenu(!isFavoriteMenuOpen);
}}
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform")}
/>
<span className="flex flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded p-0.5 ">
<Tooltip tooltipHeading="Create folder" tooltipContent="">
<FolderPlus
onClick={() => {
setCreateNewFolder(true);
!isFavoriteMenuOpen && toggleFavoriteMenu(!isFavoriteMenuOpen);
}}
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform")}
/>
</Tooltip>
<ChevronRight
onClick={() => toggleFavoriteMenu(!isFavoriteMenuOpen)}
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
@@ -168,34 +170,42 @@ export const SidebarFavoritesMenu = observer(() => {
static
>
{createNewFolder && <NewFavoriteFolder setCreateNewFolder={setCreateNewFolder} actionType="create" />}
{uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id")
.filter((fav) => !fav.parent)
.map((fav, index) => (
<Tooltip
key={fav.id}
tooltipContent={fav.entity_data ? fav.entity_data.name : fav.name}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
{fav.is_folder ? (
<FavoriteFolder
favorite={fav}
isLastChild={index === favoriteIds.length - 1}
handleRemoveFromFavorites={handleRemoveFromFavorites}
handleRemoveFromFavoritesFolder={handleRemoveFromFavoritesFolder}
/>
) : (
<FavoriteItem
favorite={fav}
handleRemoveFromFavorites={handleRemoveFromFavorites}
handleRemoveFromFavoritesFolder={handleRemoveFromFavoritesFolder}
favoriteMap={favoriteMap}
/>
)}
</Tooltip>
))}
{Object.keys(favoriteMap).length === 0 ? (
<>
{!sidebarCollapsed && (
<span className="text-custom-text-400 text-xs text-center font-medium py-1">No favorites yet</span>
)}
</>
) : (
uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id")
.filter((fav) => !fav.parent)
.map((fav, index) => (
<Tooltip
key={fav.id}
tooltipContent={fav.entity_data ? fav.entity_data.name : fav.name}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
{fav.is_folder ? (
<FavoriteFolder
favorite={fav}
isLastChild={index === favoriteIds.length - 1}
handleRemoveFromFavorites={handleRemoveFromFavorites}
handleRemoveFromFavoritesFolder={handleRemoveFromFavoritesFolder}
/>
) : (
<FavoriteItem
favorite={fav}
handleRemoveFromFavorites={handleRemoveFromFavorites}
handleRemoveFromFavoritesFolder={handleRemoveFromFavoritesFolder}
favoriteMap={favoriteMap}
/>
)}
</Tooltip>
))
)}
</Disclosure.Panel>
)}
</Transition>

View File

@@ -108,7 +108,7 @@ export const SidebarQuickActions = observer(() => {
</button>
)}
{isDraftButtonOpen && (
<div className="absolute mt-0 h-10 w-[220px] pt-2 z-10 top-8 left-0">
<div className="absolute mt-0 h-10 w-[220px] pt-2 z-[16] top-8 left-0">
<div className="h-full w-full">
<button
onClick={() => setIsDraftIssueModalOpen(true)}

View File

@@ -285,7 +285,6 @@ export class ProjectStore implements IProjectStore {
project_id: projectId,
entity_data: { name: this.projectMap[projectId].name || "" },
});
await this.fetchProjects(workspaceSlug);
return response;
} catch (error) {
console.log("Failed to add project to favorite");
@@ -306,12 +305,11 @@ export class ProjectStore implements IProjectStore {
try {
const currentProject = this.getProjectById(projectId);
if (!currentProject.is_favorite) return;
const response = await this.rootStore.favorite.removeFavoriteEntity(workspaceSlug.toString(), projectId);
runInAction(() => {
set(this.projectMap, [projectId, "is_favorite"], false);
});
await this.fetchProjects(workspaceSlug);
const response = await this.rootStore.favorite.removeFavoriteEntity(workspaceSlug.toString(), projectId);
return response;
} catch (error) {
console.log("Failed to add project to favorite");