mirror of
https://github.com/makeplane/plane.git
synced 2026-02-24 04:00:14 +01:00
Merge branch 'sync/ce-ee' of github.com:makeplane/plane-ee into develop
This commit is contained in:
@@ -304,6 +304,7 @@ class IssueRelationSerializer(BaseSerializer):
|
||||
sequence_id = serializers.IntegerField(
|
||||
source="related_issue.sequence_id", read_only=True
|
||||
)
|
||||
name = serializers.CharField(source="related_issue.name", read_only=True)
|
||||
relation_type = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -313,6 +314,7 @@ class IssueRelationSerializer(BaseSerializer):
|
||||
"project_id",
|
||||
"sequence_id",
|
||||
"relation_type",
|
||||
"name",
|
||||
]
|
||||
read_only_fields = [
|
||||
"workspace",
|
||||
@@ -328,6 +330,7 @@ class RelatedIssueSerializer(BaseSerializer):
|
||||
sequence_id = serializers.IntegerField(
|
||||
source="issue.sequence_id", read_only=True
|
||||
)
|
||||
name = serializers.CharField(source="issue.name", read_only=True)
|
||||
relation_type = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -337,6 +340,7 @@ class RelatedIssueSerializer(BaseSerializer):
|
||||
"project_id",
|
||||
"sequence_id",
|
||||
"relation_type",
|
||||
"name",
|
||||
]
|
||||
read_only_fields = [
|
||||
"workspace",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MouseEvent } from "react";
|
||||
import Link from "next/link";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useCycle, useIssues, useProject, useUser } from "hooks/store";
|
||||
import useToast from "hooks/use-toast";
|
||||
@@ -43,6 +44,7 @@ interface IActiveCycleDetails {
|
||||
export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props) => {
|
||||
// props
|
||||
const { workspaceSlug, projectId } = props;
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { currentUser } = useUser();
|
||||
const {
|
||||
@@ -76,7 +78,9 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
);
|
||||
|
||||
const emptyStateDetail = CYCLE_EMPTY_STATE_DETAILS["active"];
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", "active", currentUser?.theme.theme === "light");
|
||||
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", "active", isLightMode);
|
||||
|
||||
if (!activeCycle && isLoading)
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -18,11 +19,15 @@ export interface ICyclesBoard {
|
||||
|
||||
export const CyclesBoard: FC<ICyclesBoard> = observer((props) => {
|
||||
const { cycleIds, filter, workspaceSlug, projectId, peekCycle } = props;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { currentUser } = useUser();
|
||||
|
||||
const emptyStateDetail = CYCLE_EMPTY_STATE_DETAILS[filter as keyof typeof CYCLE_EMPTY_STATE_DETAILS];
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", filter, currentUser?.theme.theme === "light");
|
||||
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", filter, isLightMode);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -19,11 +20,15 @@ export interface ICyclesList {
|
||||
|
||||
export const CyclesList: FC<ICyclesList> = observer((props) => {
|
||||
const { cycleIds, filter, workspaceSlug, projectId } = props;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { currentUser } = useUser();
|
||||
|
||||
const emptyStateDetail = CYCLE_EMPTY_STATE_DETAILS[filter as keyof typeof CYCLE_EMPTY_STATE_DETAILS];
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", filter, currentUser?.theme.theme === "light");
|
||||
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("cycle", filter, isLightMode);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -50,11 +50,11 @@ export const DashboardWidgets = observer(() => {
|
||||
// if the widget is full width, return it in a 2 column grid
|
||||
if (widget.fullWidth)
|
||||
return (
|
||||
<div className="lg:col-span-2">
|
||||
<div key={key} className="lg:col-span-2">
|
||||
<WidgetComponent dashboardId={homeDashboardId} workspaceSlug={workspaceSlug} />
|
||||
</div>
|
||||
);
|
||||
else return <WidgetComponent dashboardId={homeDashboardId} workspaceSlug={workspaceSlug} />;
|
||||
else return <WidgetComponent key={key} dashboardId={homeDashboardId} workspaceSlug={workspaceSlug} />;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -99,7 +99,7 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
||||
</div>
|
||||
<Tab.Panels as="div" className="h-full">
|
||||
{ISSUES_TABS_LIST.map((tab) => (
|
||||
<Tab.Panel as="div" className="h-full flex flex-col">
|
||||
<Tab.Panel key={tab.key} as="div" className="h-full flex flex-col">
|
||||
<WidgetIssuesList
|
||||
issues={widgetStats.issues}
|
||||
tab={tab.key}
|
||||
|
||||
@@ -57,7 +57,7 @@ const ProjectListItem: React.FC<ProjectListItemProps> = observer((props) => {
|
||||
<div className="mt-2">
|
||||
<AvatarGroup>
|
||||
{projectDetails.members?.map((member) => (
|
||||
<Avatar src={member.member__avatar} name={member.member__display_name} />
|
||||
<Avatar key={member.member_id} src={member.member__avatar} name={member.member__display_name} />
|
||||
))}
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,7 @@ type ButtonProps = {
|
||||
hideIcon: boolean;
|
||||
hideText?: boolean;
|
||||
dropdownArrow: boolean;
|
||||
isActive?: boolean;
|
||||
dropdownArrowClassName: string;
|
||||
placeholder: string;
|
||||
tooltip: boolean;
|
||||
@@ -51,6 +52,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -60,6 +62,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -111,6 +114,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -120,6 +124,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -268,6 +273,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -279,6 +285,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "background-with-text" ? (
|
||||
@@ -310,6 +317,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -321,6 +329,7 @@ export const CycleDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useRef, useState } from "react";
|
||||
import { Combobox } from "@headlessui/react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import { usePopper } from "react-popper";
|
||||
import { CalendarDays, X } from "lucide-react";
|
||||
import { Calendar, CalendarDays, X } from "lucide-react";
|
||||
// hooks
|
||||
import { useDropdownKeyDown } from "hooks/use-dropdown-key-down";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
@@ -23,6 +23,7 @@ type Props = TDropdownProps & {
|
||||
onChange: (val: Date | null) => void;
|
||||
value: Date | string | null;
|
||||
closeOnSelect?: boolean;
|
||||
showPlaceholderIcon?: boolean;
|
||||
};
|
||||
|
||||
type ButtonProps = {
|
||||
@@ -33,9 +34,11 @@ type ButtonProps = {
|
||||
isClearable: boolean;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
onClear: () => void;
|
||||
placeholder: string;
|
||||
tooltip: boolean;
|
||||
showPlaceholderIcon?: boolean;
|
||||
};
|
||||
|
||||
const BorderButton = (props: ButtonProps) => {
|
||||
@@ -47,6 +50,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
isClearable,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
onClear,
|
||||
placeholder,
|
||||
tooltip,
|
||||
@@ -61,6 +65,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -131,9 +136,11 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
isClearable,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
onClear,
|
||||
placeholder,
|
||||
tooltip,
|
||||
showPlaceholderIcon = false,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
@@ -145,11 +152,16 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
{!hideIcon && icon}
|
||||
{!hideText && <span className="flex-grow truncate">{date ? renderFormattedDate(date) : placeholder}</span>}
|
||||
{showPlaceholderIcon && !date && (
|
||||
<Calendar className="h-2.5 w-2.5 flex-shrink-0 hidden group-hover:inline text-custom-text-400" />
|
||||
)}
|
||||
|
||||
{isClearable && (
|
||||
<X
|
||||
className={cn("h-2 w-2 flex-shrink-0", clearIconClassName)}
|
||||
@@ -183,6 +195,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
placement,
|
||||
tabIndex,
|
||||
tooltip = false,
|
||||
showPlaceholderIcon = false,
|
||||
value,
|
||||
} = props;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -246,6 +259,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
placeholder={placeholder}
|
||||
isClearable={isClearable && isDateSelected}
|
||||
onClear={() => onChange(null)}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -258,6 +272,7 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
placeholder={placeholder}
|
||||
isClearable={isClearable && isDateSelected}
|
||||
onClear={() => onChange(null)}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
@@ -296,7 +311,9 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
placeholder={placeholder}
|
||||
isClearable={isClearable && isDateSelected}
|
||||
onClear={() => onChange(null)}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
showPlaceholderIcon={showPlaceholderIcon}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
<TransparentButton
|
||||
@@ -308,8 +325,10 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
placeholder={placeholder}
|
||||
isClearable={isClearable && isDateSelected}
|
||||
onClear={() => onChange(null)}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
showPlaceholderIcon={showPlaceholderIcon}
|
||||
/>
|
||||
) : null}
|
||||
</button>
|
||||
|
||||
@@ -31,6 +31,7 @@ type ButtonProps = {
|
||||
dropdownArrowClassName: string;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
placeholder: string;
|
||||
tooltip: boolean;
|
||||
};
|
||||
@@ -51,6 +52,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -64,6 +66,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -123,6 +126,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -136,6 +140,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -276,6 +281,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -287,6 +293,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "background-with-text" ? (
|
||||
@@ -318,6 +325,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -329,6 +337,7 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -14,6 +14,7 @@ type ButtonProps = {
|
||||
placeholder: string;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
tooltip: boolean;
|
||||
userIds: string | string[] | null;
|
||||
};
|
||||
@@ -50,6 +51,7 @@ export const BorderButton = observer((props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
userIds,
|
||||
tooltip,
|
||||
@@ -57,7 +59,7 @@ export const BorderButton = observer((props: ButtonProps) => {
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
||||
const isMultiple = Array.isArray(userIds);
|
||||
const isArray = Array.isArray(userIds);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -68,13 +70,18 @@ export const BorderButton = observer((props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate">
|
||||
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
|
||||
<span className="flex-grow truncate text-sm leading-5">
|
||||
{isArray && userIds.length > 0
|
||||
? userIds.length === 1
|
||||
? getUserDetails(userIds[0])?.display_name
|
||||
: ""
|
||||
: placeholder}
|
||||
</span>
|
||||
)}
|
||||
{dropdownArrow && (
|
||||
@@ -99,7 +106,7 @@ export const BackgroundButton = observer((props: ButtonProps) => {
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
||||
const isMultiple = Array.isArray(userIds);
|
||||
const isArray = Array.isArray(userIds);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -115,8 +122,12 @@ export const BackgroundButton = observer((props: ButtonProps) => {
|
||||
>
|
||||
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate">
|
||||
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
|
||||
<span className="flex-grow truncate text-sm leading-5">
|
||||
{isArray && userIds.length > 0
|
||||
? userIds.length === 1
|
||||
? getUserDetails(userIds[0])?.display_name
|
||||
: ""
|
||||
: placeholder}
|
||||
</span>
|
||||
)}
|
||||
{dropdownArrow && (
|
||||
@@ -134,6 +145,7 @@ export const TransparentButton = observer((props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
placeholder,
|
||||
userIds,
|
||||
tooltip,
|
||||
@@ -141,7 +153,7 @@ export const TransparentButton = observer((props: ButtonProps) => {
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
|
||||
const isMultiple = Array.isArray(userIds);
|
||||
const isArray = Array.isArray(userIds);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
@@ -152,13 +164,18 @@ export const TransparentButton = observer((props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
{!hideIcon && <ButtonAvatars tooltip={tooltip} userIds={userIds} />}
|
||||
{!hideText && (
|
||||
<span className="flex-grow truncate">
|
||||
{userIds ? (isMultiple ? placeholder : getUserDetails(userIds)?.display_name) : placeholder}
|
||||
<span className="flex-grow truncate text-sm leading-5">
|
||||
{isArray && userIds.length > 0
|
||||
? userIds.length === 1
|
||||
? getUserDetails(userIds[0])?.display_name
|
||||
: ""
|
||||
: placeholder}
|
||||
</span>
|
||||
)}
|
||||
{dropdownArrow && (
|
||||
|
||||
@@ -147,6 +147,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -157,6 +158,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
@@ -189,6 +191,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -199,6 +202,7 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,7 @@ type ButtonProps = {
|
||||
dropdownArrowClassName: string;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
module: IModule | null;
|
||||
placeholder: string;
|
||||
tooltip: boolean;
|
||||
@@ -50,6 +51,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
module,
|
||||
placeholder,
|
||||
tooltip,
|
||||
@@ -60,6 +62,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -110,6 +113,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
module,
|
||||
placeholder,
|
||||
tooltip,
|
||||
@@ -120,6 +124,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{ "bg-custom-background-80": isActive },
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -267,6 +272,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -278,6 +284,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "background-with-text" ? (
|
||||
@@ -309,6 +316,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -320,6 +328,7 @@ export const ModuleDropdown: React.FC<Props> = observer((props) => {
|
||||
hideIcon={hideIcon}
|
||||
hideText
|
||||
placeholder={placeholder}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -31,6 +31,7 @@ type ButtonProps = {
|
||||
dropdownArrowClassName: string;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
highlightUrgent: boolean;
|
||||
priority: TIssuePriorities;
|
||||
tooltip: boolean;
|
||||
@@ -181,6 +182,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
highlightUrgent,
|
||||
priority,
|
||||
tooltip,
|
||||
@@ -207,6 +209,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
"px-0.5": hideText,
|
||||
// highlight the whole button if text is hidden and priority is urgent
|
||||
"bg-red-500 border-red-500": priority === "urgent" && hideText && highlightUrgent,
|
||||
"bg-custom-background-80": isActive,
|
||||
},
|
||||
className
|
||||
)}
|
||||
@@ -312,7 +315,13 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
||||
as="div"
|
||||
ref={dropdownRef}
|
||||
tabIndex={tabIndex}
|
||||
className={cn("h-full", className)}
|
||||
className={cn(
|
||||
"h-full",
|
||||
{
|
||||
"bg-custom-background-80": isOpen,
|
||||
},
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
@@ -402,6 +411,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -414,6 +424,7 @@ export const PriorityDropdown: React.FC<Props> = (props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
|
||||
@@ -30,6 +30,7 @@ type ButtonProps = {
|
||||
dropdownArrowClassName: string;
|
||||
hideIcon?: boolean;
|
||||
hideText?: boolean;
|
||||
isActive?: boolean;
|
||||
state: IState | undefined;
|
||||
tooltip: boolean;
|
||||
};
|
||||
@@ -41,6 +42,7 @@ const BorderButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
state,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -50,6 +52,9 @@ const BorderButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 rounded text-xs px-2 py-0.5",
|
||||
{
|
||||
"bg-custom-background-80": isActive,
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -111,6 +116,7 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
dropdownArrowClassName,
|
||||
hideIcon = false,
|
||||
hideText = false,
|
||||
isActive = false,
|
||||
state,
|
||||
tooltip,
|
||||
} = props;
|
||||
@@ -120,6 +126,9 @@ const TransparentButton = (props: ButtonProps) => {
|
||||
<div
|
||||
className={cn(
|
||||
"h-full flex items-center gap-1.5 rounded text-xs px-2 py-0.5 hover:bg-custom-background-80",
|
||||
{
|
||||
"bg-custom-background-80": isActive,
|
||||
},
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -251,6 +260,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "border-without-text" ? (
|
||||
@@ -260,6 +270,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
@@ -289,6 +300,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
/>
|
||||
) : buttonVariant === "transparent-without-text" ? (
|
||||
@@ -298,6 +310,7 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
||||
dropdownArrow={dropdownArrow && !disabled}
|
||||
dropdownArrowClassName={dropdownArrowClassName}
|
||||
hideIcon={hideIcon}
|
||||
isActive={isOpen}
|
||||
tooltip={tooltip}
|
||||
hideText
|
||||
/>
|
||||
|
||||
@@ -64,6 +64,7 @@ export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props)
|
||||
{
|
||||
"cursor-not-allowed": disabled,
|
||||
"hover:bg-custom-background-80": !disabled,
|
||||
"bg-custom-background-80": isParentIssueModalOpen,
|
||||
},
|
||||
className
|
||||
)}
|
||||
@@ -72,15 +73,20 @@ export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props)
|
||||
>
|
||||
{issue.parent_id && parentIssue ? (
|
||||
<div className="flex items-center gap-1 bg-green-500/20 text-green-700 rounded px-1.5 py-1">
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${parentIssue?.id}`}
|
||||
className="text-xs font-medium"
|
||||
>
|
||||
{parentIssueProjectDetails?.identifier}-{parentIssue.sequence_id}
|
||||
</Link>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={parentIssue.name}>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectId}/issues/${parentIssue?.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs font-medium mt-0.5"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{parentIssueProjectDetails?.identifier}-{parentIssue.sequence_id}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
|
||||
{!disabled && (
|
||||
<Tooltip tooltipContent="Remove">
|
||||
<Tooltip tooltipContent="Remove" position="bottom">
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -96,7 +102,15 @@ export const IssueParentSelect: React.FC<TIssueParentSelect> = observer((props)
|
||||
) : (
|
||||
<span className="text-sm text-custom-text-400">Add parent issue</span>
|
||||
)}
|
||||
{!disabled && <Pencil className="h-4 w-4 flex-shrink-0 hidden group-hover:inline" />}
|
||||
{!disabled && (
|
||||
<span
|
||||
className={cn("p-1 flex-shrink-0 opacity-0 group-hover:opacity-100", {
|
||||
"text-custom-text-400": !issue.parent_id && !parentIssue,
|
||||
})}
|
||||
>
|
||||
<Pencil className="h-2.5 w-2.5 flex-shrink-0" />
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { CircleDot, CopyPlus, Pencil, X, XCircle } from "lucide-react";
|
||||
// hooks
|
||||
@@ -101,59 +102,72 @@ export const IssueRelationSelect: React.FC<TIssueRelationSelect> = observer((pro
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"group flex items-center justify-between gap-2 px-2 py-0.5 rounded outline-none",
|
||||
"group flex items-center gap-2 px-2 py-0.5 rounded outline-none",
|
||||
{
|
||||
"cursor-not-allowed": disabled,
|
||||
"hover:bg-custom-background-80": !disabled,
|
||||
"bg-custom-background-80": isRelationModalOpen === relationKey,
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={() => toggleRelationModal(relationKey)}
|
||||
disabled={disabled}
|
||||
>
|
||||
{relationIssueIds.length > 0 ? (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{relationIssueIds.map((relationIssueId) => {
|
||||
const currentIssue = issueMap[relationIssueId];
|
||||
if (!currentIssue) return;
|
||||
<div className="flex items-start justify-between w-full">
|
||||
{relationIssueIds.length > 0 ? (
|
||||
<div className="flex items-center gap-2 py-0.5 flex-wrap">
|
||||
{relationIssueIds.map((relationIssueId) => {
|
||||
const currentIssue = issueMap[relationIssueId];
|
||||
if (!currentIssue) return;
|
||||
|
||||
const projectDetails = getProjectById(currentIssue.project_id);
|
||||
const projectDetails = getProjectById(currentIssue.project_id);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={relationIssueId}
|
||||
className={`group flex items-center gap-1 rounded px-1.5 py-1 ${issueRelationObject[relationKey].className}`}
|
||||
>
|
||||
<a
|
||||
href={`/${workspaceSlug}/projects/${projectDetails?.id}/issues/${currentIssue.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs font-medium"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
return (
|
||||
<div
|
||||
key={relationIssueId}
|
||||
className={`group flex items-center gap-1 rounded px-1.5 pt-1 pb-1 leading-3 hover:bg-custom-background-90 ${issueRelationObject[relationKey].className}`}
|
||||
>
|
||||
{`${projectDetails?.identifier}-${currentIssue?.sequence_id}`}
|
||||
</a>
|
||||
{!disabled && (
|
||||
<Tooltip tooltipContent="Remove">
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
removeRelation(workspaceSlug, projectId, issueId, relationKey, relationIssueId);
|
||||
}}
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={currentIssue.name}>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectDetails?.id}/issues/${currentIssue.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs font-medium mt-0.5"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<X className="h-2.5 w-2.5 text-custom-text-300 hover:text-red-500" />
|
||||
</span>
|
||||
{`${projectDetails?.identifier}-${currentIssue?.sequence_id}`}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-custom-text-400">{issueRelationObject[relationKey].placeholder}</span>
|
||||
)}
|
||||
{!disabled && <Pencil className="h-4 w-4 flex-shrink-0 hidden group-hover:inline" />}
|
||||
{!disabled && (
|
||||
<Tooltip tooltipContent="Remove" position="bottom">
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
removeRelation(workspaceSlug, projectId, issueId, relationKey, relationIssueId);
|
||||
}}
|
||||
>
|
||||
<X className="h-2.5 w-2.5 text-custom-text-300 hover:text-red-500" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-sm text-custom-text-400">{issueRelationObject[relationKey].placeholder}</span>
|
||||
)}
|
||||
{!disabled && (
|
||||
<span
|
||||
className={cn("p-1 flex-shrink-0 opacity-0 group-hover:opacity-100", {
|
||||
"text-custom-text-400": relationIssueIds.length === 0,
|
||||
})}
|
||||
>
|
||||
<Pencil className="h-2.5 w-2.5 flex-shrink-0" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -184,7 +184,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
projectId={projectId?.toString() ?? ""}
|
||||
placeholder="Add assignees"
|
||||
multiple
|
||||
buttonVariant={issue?.assignee_ids?.length > 0 ? "transparent-without-text" : "transparent-with-text"}
|
||||
buttonVariant={issue?.assignee_ids?.length > 1 ? "transparent-without-text" : "transparent-with-text"}
|
||||
className="w-3/5 flex-grow group"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={`text-sm justify-between ${
|
||||
@@ -233,6 +233,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
buttonClassName={`text-sm ${issue?.start_date ? "" : "text-custom-text-400"}`}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline"
|
||||
showPlaceholderIcon
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -257,6 +258,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
buttonClassName={`text-sm ${issue?.target_date ? "" : "text-custom-text-400"}`}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline"
|
||||
showPlaceholderIcon
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -332,8 +334,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 min-h-8">
|
||||
<div className="flex items-center gap-1 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-2 min-h-8">
|
||||
<div className="flex gap-1 pt-2 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<RelatedIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Relates to</span>
|
||||
</div>
|
||||
@@ -347,8 +349,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 min-h-8">
|
||||
<div className="flex items-center gap-1 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-2 min-h-8">
|
||||
<div className="flex gap-1 pt-2 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<XCircle className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Blocking</span>
|
||||
</div>
|
||||
@@ -362,8 +364,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 min-h-8">
|
||||
<div className="flex items-center gap-1 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-2 min-h-8">
|
||||
<div className="flex gap-1 pt-2 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<CircleDot className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Blocked by</span>
|
||||
</div>
|
||||
@@ -377,8 +379,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 min-h-8">
|
||||
<div className="flex items-center gap-1 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-2 min-h-8">
|
||||
<div className="flex gap-1 pt-2 w-2/5 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<CopyPlus className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Duplicate of</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
import size from "lodash/size";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -26,6 +27,9 @@ export const ProjectArchivedEmptyState: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
currentUser,
|
||||
@@ -35,12 +39,9 @@ export const ProjectArchivedEmptyState: React.FC = observer(() => {
|
||||
const userFilters = issuesFilter?.issueFilters?.filters;
|
||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath(
|
||||
"empty-filters",
|
||||
activeLayout ?? "list",
|
||||
currentUser?.theme.theme === "light"
|
||||
);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("archived", "empty-issues", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath("empty-filters", activeLayout ?? "list", isLightMode);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("archived", "empty-issues", isLightMode);
|
||||
|
||||
const issueFilterCount = size(
|
||||
Object.fromEntries(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
import size from "lodash/size";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -26,6 +27,9 @@ export const ProjectDraftEmptyState: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
currentUser,
|
||||
@@ -35,12 +39,9 @@ export const ProjectDraftEmptyState: React.FC = observer(() => {
|
||||
const userFilters = issuesFilter?.issueFilters?.filters;
|
||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath(
|
||||
"empty-filters",
|
||||
activeLayout ?? "list",
|
||||
currentUser?.theme.theme === "light"
|
||||
);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("draft", "empty-issues", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath("empty-filters", activeLayout ?? "list", isLightMode);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("draft", "empty-issues", isLightMode);
|
||||
|
||||
const issueFilterCount = size(
|
||||
Object.fromEntries(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
import size from "lodash/size";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useIssues, useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -26,6 +27,8 @@ export const ProjectEmptyState: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: commandPaletteStore,
|
||||
@@ -40,12 +43,9 @@ export const ProjectEmptyState: React.FC = observer(() => {
|
||||
const userFilters = issuesFilter?.issueFilters?.filters;
|
||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath(
|
||||
"empty-filters",
|
||||
activeLayout ?? "list",
|
||||
currentUser?.theme.theme === "light"
|
||||
);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "issues", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const currentLayoutEmptyStateImagePath = getEmptyStateImagePath("empty-filters", activeLayout ?? "list", isLightMode);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "issues", isLightMode);
|
||||
|
||||
const issueFilterCount = size(
|
||||
Object.fromEntries(
|
||||
|
||||
@@ -28,7 +28,7 @@ export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, userId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
@@ -29,7 +29,7 @@ export const ProfileIssuesListLayout: FC = observer(() => {
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, userId, issue.id, issue);
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, userId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
@@ -82,17 +82,17 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
if (storeLabels && storeLabels.length > 0) projectLabels = storeLabels;
|
||||
|
||||
const options = projectLabels.map((label) => ({
|
||||
value: label.id,
|
||||
query: label.name,
|
||||
value: label?.id,
|
||||
query: label?.name,
|
||||
content: (
|
||||
<div className="flex items-center justify-start gap-2 overflow-hidden">
|
||||
<span
|
||||
className="h-2.5 w-2.5 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor: label.color,
|
||||
backgroundColor: label?.color,
|
||||
}}
|
||||
/>
|
||||
<div className="line-clamp-1 inline-block truncate">{label.name}</div>
|
||||
<div className="line-clamp-1 inline-block truncate">{label?.name}</div>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
@@ -106,11 +106,11 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
value.length <= maxRender ? (
|
||||
<>
|
||||
{projectLabels
|
||||
?.filter((l) => value.includes(l.id))
|
||||
?.filter((l) => value.includes(l?.id))
|
||||
.map((label) => (
|
||||
<Tooltip position="top" tooltipHeading="Labels" tooltipContent={label.name ?? ""}>
|
||||
<Tooltip position="top" tooltipHeading="Labels" tooltipContent={label?.name ?? ""}>
|
||||
<div
|
||||
key={label.id}
|
||||
key={label?.id}
|
||||
className={`flex overflow-hidden hover:bg-custom-background-80 ${
|
||||
!disabled && "cursor-pointer"
|
||||
} h-full max-w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 px-2.5 py-1 text-xs`}
|
||||
@@ -122,7 +122,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
backgroundColor: label?.color ?? "#000000",
|
||||
}}
|
||||
/>
|
||||
<div className="line-clamp-1 inline-block w-auto max-w-[100px] truncate">{label.name}</div>
|
||||
<div className="line-clamp-1 inline-block w-auto max-w-[100px] truncate">{label?.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -138,8 +138,8 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
||||
position="top"
|
||||
tooltipHeading="Labels"
|
||||
tooltipContent={projectLabels
|
||||
?.filter((l) => value.includes(l.id))
|
||||
.map((l) => l.name)
|
||||
?.filter((l) => value.includes(l?.id))
|
||||
.map((l) => l?.name)
|
||||
.join(", ")}
|
||||
>
|
||||
<div className="flex h-full items-center gap-1.5 text-custom-text-200">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useGlobalView, useIssues, useProject, useUser } from "hooks/store";
|
||||
import { useWorkspaceIssueProperties } from "hooks/use-workspace-issue-properties";
|
||||
@@ -25,6 +26,8 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, globalViewId } = router.query;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
//swr hook for fetching issue properties
|
||||
useWorkspaceIssueProperties(workspaceSlug);
|
||||
// store
|
||||
@@ -46,7 +49,8 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
const currentView = isDefaultView ? groupedIssueIds.dataViewId : "custom-view";
|
||||
const currentViewDetails = ALL_ISSUES_EMPTY_STATE_DETAILS[currentView as keyof typeof ALL_ISSUES_EMPTY_STATE_DETAILS];
|
||||
|
||||
const emptyStateImage = getEmptyStateImagePath("all-issues", currentView, currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("all-issues", currentView, isLightMode);
|
||||
|
||||
// filter init from the query params
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
projectId={projectId}
|
||||
placeholder="Add assignees"
|
||||
multiple
|
||||
buttonVariant={issue?.assignee_ids?.length > 0 ? "transparent-without-text" : "transparent-with-text"}
|
||||
buttonVariant={issue?.assignee_ids?.length > 1 ? "transparent-without-text" : "transparent-with-text"}
|
||||
className="w-3/4 flex-grow group"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName={`text-sm justify-between ${issue?.assignee_ids.length > 0 ? "" : "text-custom-text-400"}`}
|
||||
@@ -148,6 +148,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
buttonClassName={`text-sm ${issue?.start_date ? "" : "text-custom-text-400"}`}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline"
|
||||
showPlaceholderIcon
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -173,6 +174,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
buttonClassName={`text-sm ${issue?.target_date ? "" : "text-custom-text-400"}`}
|
||||
hideIcon
|
||||
clearIconClassName="h-3 w-3 hidden group-hover:inline"
|
||||
showPlaceholderIcon
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -251,8 +253,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
</div>
|
||||
|
||||
{/* relates to */}
|
||||
<div className="flex items-center gap-3 min-h-8">
|
||||
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-3 min-h-8">
|
||||
<div className="flex pt-2 gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<RelatedIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Relates to</span>
|
||||
</div>
|
||||
@@ -267,8 +269,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
</div>
|
||||
|
||||
{/* blocking */}
|
||||
<div className="flex items-center gap-3 min-h-8">
|
||||
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-3 min-h-8">
|
||||
<div className="flex pt-2 gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<XCircle className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Blocking</span>
|
||||
</div>
|
||||
@@ -283,8 +285,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
</div>
|
||||
|
||||
{/* blocked by */}
|
||||
<div className="flex items-center gap-3 min-h-8">
|
||||
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-3 min-h-8">
|
||||
<div className="flex pt-2 gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<CircleDot className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Blocked by</span>
|
||||
</div>
|
||||
@@ -299,8 +301,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
</div>
|
||||
|
||||
{/* duplicate of */}
|
||||
<div className="flex items-center gap-3 min-h-8">
|
||||
<div className="flex items-center gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<div className="flex gap-3 min-h-8">
|
||||
<div className="flex pt-2 gap-1 w-1/4 flex-shrink-0 text-sm text-custom-text-300">
|
||||
<CopyPlus className="h-4 w-4 flex-shrink-0" />
|
||||
<span>Duplicate of</span>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useModule, useUser } from "hooks/store";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
@@ -15,6 +16,8 @@ export const ModulesListView: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, peekModule } = router.query;
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { commandPalette: commandPaletteStore } = useApplication();
|
||||
const {
|
||||
@@ -25,7 +28,8 @@ export const ModulesListView: React.FC = observer(() => {
|
||||
|
||||
const { storedValue: modulesView } = useLocalStorage("modules_view", "grid");
|
||||
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "modules", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "modules", isLightMode);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ export const NotificationHeader: React.FC<NotificationHeaderProps> = (props) =>
|
||||
<MoreVertical className="h-3.5 w-3.5" />
|
||||
</div>
|
||||
}
|
||||
closeOnSelect
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={markAllNotificationsAsRead}>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useApplication, useDashboard, useProject, useUser } from "hooks/store";
|
||||
@@ -14,6 +15,8 @@ import { Spinner } from "@plane/ui";
|
||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
|
||||
export const WorkspaceDashboardView = observer(() => {
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: { toggleCreateProjectModal },
|
||||
@@ -28,7 +31,8 @@ export const WorkspaceDashboardView = observer(() => {
|
||||
const { homeDashboardId, fetchHomeDashboardWidgets } = useDashboard();
|
||||
const { joinedProjectIds } = useProject();
|
||||
|
||||
const emptyStateImage = getEmptyStateImagePath("onboarding", "dashboard", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("onboarding", "dashboard", isLightMode);
|
||||
|
||||
const handleTourCompleted = () => {
|
||||
updateTourCompleted()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useUser } from "hooks/store";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
@@ -18,7 +19,9 @@ type IPagesListView = {
|
||||
|
||||
export const PagesListView: FC<IPagesListView> = (props) => {
|
||||
const { pageIds: projectPageIds } = props;
|
||||
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: { toggleCreatePageModal },
|
||||
} = useApplication();
|
||||
@@ -36,11 +39,8 @@ export const PagesListView: FC<IPagesListView> = (props) => {
|
||||
? PAGE_EMPTY_STATE_DETAILS[pageTab as keyof typeof PAGE_EMPTY_STATE_DETAILS]
|
||||
: PAGE_EMPTY_STATE_DETAILS["All"];
|
||||
|
||||
const emptyStateImage = getEmptyStateImagePath(
|
||||
"pages",
|
||||
currentPageTabDetails.key,
|
||||
currentUser?.theme.theme === "light"
|
||||
);
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("pages", currentPageTabDetails.key, isLightMode);
|
||||
|
||||
const isButtonVisible = currentPageTabDetails.key !== "archived" && currentPageTabDetails.key !== "favorites";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useUser } from "hooks/store";
|
||||
import { useProjectPages } from "hooks/store/use-project-specific-pages";
|
||||
@@ -14,6 +15,8 @@ import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
|
||||
export const RecentPagesList: FC = observer(() => {
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { commandPalette: commandPaletteStore } = useApplication();
|
||||
const {
|
||||
@@ -22,7 +25,8 @@ export const RecentPagesList: FC = observer(() => {
|
||||
} = useUser();
|
||||
const { recentProjectPages } = useProjectPages();
|
||||
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("pages", "recent", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("pages", "recent", isLightMode);
|
||||
|
||||
// FIXME: replace any with proper type
|
||||
const isEmpty = recentProjectPages && Object.values(recentProjectPages).every((value: any) => value.length === 0);
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// components
|
||||
import { ProfileIssuesListLayout } from "components/issues/issue-layouts/list/roots/profile-issues-root";
|
||||
import { ProfileIssuesKanBanLayout } from "components/issues/issue-layouts/kanban/roots/profile-issues-root";
|
||||
@@ -27,6 +28,9 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
||||
workspaceSlug: string;
|
||||
userId: string;
|
||||
};
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
currentUser,
|
||||
@@ -46,7 +50,8 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => {
|
||||
}
|
||||
);
|
||||
|
||||
const emptyStateImage = getEmptyStateImagePath("profile", type, currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("profile", type, isLightMode);
|
||||
|
||||
const activeLayout = issueFilters?.displayFilters?.layout || undefined;
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useProject, useUser } from "hooks/store";
|
||||
// components
|
||||
import { ProjectCard } from "components/project";
|
||||
import { Loader } from "@plane/ui";
|
||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||
// icons
|
||||
import { Plus } from "lucide-react";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
|
||||
export const ProjectCardList = observer(() => {
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: commandPaletteStore,
|
||||
@@ -22,7 +23,8 @@ export const ProjectCardList = observer(() => {
|
||||
} = useUser();
|
||||
const { workspaceProjectIds, searchedProjects, getProjectById } = useProject();
|
||||
|
||||
const emptyStateImage = getEmptyStateImagePath("onboarding", "projects", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const emptyStateImage = getEmptyStateImagePath("onboarding", "projects", isLightMode);
|
||||
|
||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ export const ProjectSidebarList: FC = observer(() => {
|
||||
type="button"
|
||||
className="group flex w-full items-center gap-1 whitespace-nowrap rounded px-1.5 text-left text-sm font-semibold text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80"
|
||||
>
|
||||
Projects
|
||||
Your projects
|
||||
{open ? (
|
||||
<ChevronDown className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Search } from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useProjectView, useUser } from "hooks/store";
|
||||
// components
|
||||
@@ -14,6 +15,8 @@ import { EUserProjectRoles } from "constants/project";
|
||||
export const ProjectViewsList = observer(() => {
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: { toggleCreateViewModal },
|
||||
@@ -43,7 +46,8 @@ export const ProjectViewsList = observer(() => {
|
||||
|
||||
const viewsList = projectViewIds.map((viewId) => getViewById(viewId));
|
||||
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "views", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "views", isLightMode);
|
||||
|
||||
const filteredViewsList = viewsList.filter((v) => v?.name.toLowerCase().includes(query.toLowerCase()));
|
||||
|
||||
|
||||
@@ -304,7 +304,17 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
},
|
||||
my_issues: {
|
||||
spreadsheet: {
|
||||
filters: ["priority", "state_group", "labels", "assignees", "created_by", "project", "start_date", "target_date"],
|
||||
filters: [
|
||||
"priority",
|
||||
"state_group",
|
||||
"labels",
|
||||
"assignees",
|
||||
"created_by",
|
||||
"subscriber",
|
||||
"project",
|
||||
"start_date",
|
||||
"target_date",
|
||||
],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
type: [null, "active", "backlog"],
|
||||
|
||||
@@ -264,6 +264,13 @@ const useUserNotification = () => {
|
||||
|
||||
await userNotificationServices
|
||||
.markAllNotificationsAsRead(workspaceSlug.toString(), markAsReadParams)
|
||||
.then(() => {
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
title: "Success!",
|
||||
message: "All Notifications marked as read.",
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Fragment, ReactElement } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useProject, useUser } from "hooks/store";
|
||||
// layouts
|
||||
@@ -16,6 +17,8 @@ import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
|
||||
const AnalyticsPage: NextPageWithLayout = observer(() => {
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
commandPalette: { toggleCreateProjectModal },
|
||||
@@ -27,7 +30,8 @@ const AnalyticsPage: NextPageWithLayout = observer(() => {
|
||||
} = useUser();
|
||||
const { workspaceProjectIds } = useProject();
|
||||
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "analytics", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "analytics", isLightMode);
|
||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Fragment, useCallback, useState, ReactElement } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useCycle, useUser } from "hooks/store";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
@@ -22,6 +23,8 @@ import { EUserWorkspaceRoles } from "constants/workspace";
|
||||
|
||||
const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
||||
const [createModal, setCreateModal] = useState(false);
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
@@ -49,7 +52,9 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
||||
},
|
||||
[handleCurrentLayout, setCycleTab]
|
||||
);
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "cycles", currentUser?.theme.theme === "light");
|
||||
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "cycles", isLightMode);
|
||||
|
||||
const totalCycles = currentProjectCycleIds?.length ?? 0;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import dynamic from "next/dynamic";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import useSWR from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useTheme } from "next-themes";
|
||||
// hooks
|
||||
import { useApplication, useUser } from "hooks/store";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
@@ -48,7 +49,9 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// states
|
||||
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
|
||||
// store
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const {
|
||||
currentUser,
|
||||
currentUserLoader,
|
||||
@@ -94,7 +97,8 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
||||
}
|
||||
};
|
||||
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "pages", currentUser?.theme.theme === "light");
|
||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "pages", isLightMode);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
|
||||
@@ -76,6 +76,8 @@ export class IssueFilterHelperStore implements IIssueFilterHelperStore {
|
||||
labels: filters?.labels || undefined,
|
||||
start_date: filters?.start_date || undefined,
|
||||
target_date: filters?.target_date || undefined,
|
||||
project: filters.project || undefined,
|
||||
subscriber: filters.subscriber || undefined,
|
||||
// display filters
|
||||
type: displayFilters?.type || undefined,
|
||||
sub_issue: displayFilters?.sub_issue ?? true,
|
||||
|
||||
Reference in New Issue
Block a user