diff --git a/apiserver/plane/space/views/state.py b/apiserver/plane/space/views/state.py index 853bf022c2..7ffcef5b9d 100644 --- a/apiserver/plane/space/views/state.py +++ b/apiserver/plane/space/views/state.py @@ -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, diff --git a/packages/types/src/favorite/favorite.d.ts b/packages/types/src/favorite/favorite.d.ts index 154d0e2661..092a120959 100644 --- a/packages/types/src/favorite/favorite.d.ts +++ b/packages/types/src/favorite/favorite.d.ts @@ -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; diff --git a/space/core/components/issues/issue-layouts/utils.tsx b/space/core/components/issues/issue-layouts/utils.tsx index edb67b4f71..992f6367c9 100644 --- a/space/core/components/issues/issue-layouts/utils.tsx +++ b/space/core/components/issues/issue-layouts/utils.tsx @@ -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: ( diff --git a/space/core/store/state.store.ts b/space/core/store/state.store.ts index 36a0ceaf7a..aff22a22a8 100644 --- a/space/core/store/state.store.ts +++ b/space/core/store/state.store.ts @@ -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) => { diff --git a/space/helpers/state.helper.ts b/space/helpers/state.helper.ts new file mode 100644 index 0000000000..81bffdef96 --- /dev/null +++ b/space/helpers/state.helper.ts @@ -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); + }); +}; diff --git a/web/core/components/project/create-project-form.tsx b/web/core/components/project/create-project-form.tsx index 65c620635c..53de729796 100644 --- a/web/core/components/project/create-project-form.tsx +++ b/web/core/components/project/create-project-form.tsx @@ -294,7 +294,7 @@ export const CreateProjectForm: FC = observer((props) => { /> diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 737f819835..332c752849 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -298,7 +298,7 @@ export const ProjectDetailsForm: FC = (props) => { /> diff --git a/web/core/components/workspace/sidebar/favorites/favorite-item.tsx b/web/core/components/workspace/sidebar/favorites/favorite-item.tsx index 79cd8dc563..f824fc493f 100644 --- a/web/core/components/workspace/sidebar/favorites/favorite-item.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorite-item.tsx @@ -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 = { + page: , + project: , + view: , + module: , + cycle: , + issue: , + folder: , +}; + export const FavoriteItem = observer( ({ favoriteMap, @@ -48,28 +68,18 @@ export const FavoriteItem = observer( const dragHandleRef = useRef(null); const actionSectionRef = useRef(null); - const getIcon = () => { - const className = `flex-shrink-0 size-4 stroke-[1.5] m-auto`; - - switch (favorite.entity_type) { - case "page": - return ; - case "project": - return ; - case "view": - return ; - case "module": - return ; - case "cycle": - return ; - case "issue": - return ; - case "folder": - return ; - default: - return ; - } - }; + const getIcon = () => ( + <> +
{ICONS[favorite.entity_type] || }
+
+ {favorite.entity_data?.logo_props?.in_use ? ( + + ) : ( + ICONS[favorite.entity_type] || + )} +
+ + ); const getLink = () => { switch (favorite.entity_type) { @@ -133,7 +143,10 @@ export const FavoriteItem = observer(
{ toggleFavoriteMenu(!isFavoriteMenuOpen)} className="flex-1 text-start"> YOUR FAVORITES - - { - setCreateNewFolder(true); - !isFavoriteMenuOpen && toggleFavoriteMenu(!isFavoriteMenuOpen); - }} - className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform")} - /> + + + { + setCreateNewFolder(true); + !isFavoriteMenuOpen && toggleFavoriteMenu(!isFavoriteMenuOpen); + }} + className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform")} + /> + 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 && } - {uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id") - .filter((fav) => !fav.parent) - .map((fav, index) => ( - - {fav.is_folder ? ( - - ) : ( - - )} - - ))} + {Object.keys(favoriteMap).length === 0 ? ( + <> + {!sidebarCollapsed && ( + No favorites yet + )} + + ) : ( + uniqBy(orderBy(Object.values(favoriteMap), "sequence", "desc"), "id") + .filter((fav) => !fav.parent) + .map((fav, index) => ( + + {fav.is_folder ? ( + + ) : ( + + )} + + )) + )} )} diff --git a/web/core/components/workspace/sidebar/quick-actions.tsx b/web/core/components/workspace/sidebar/quick-actions.tsx index 30f20e4da2..f0eca5cf09 100644 --- a/web/core/components/workspace/sidebar/quick-actions.tsx +++ b/web/core/components/workspace/sidebar/quick-actions.tsx @@ -108,7 +108,7 @@ export const SidebarQuickActions = observer(() => { )} {isDraftButtonOpen && ( -
+