mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: issue property dropdown data flow (#3425)
* dev: workspace states and estimates * refactor issue dropdown logic to help work properly with issues on global level * fix: project labels response change * fix label type * change store computed actions to computed functions from mobx-utils * fix: state response change * chore: project and workspace state change * fix state and label types * chore: state and label serializer change * modify state and label types * fix dropdown reset on project id change * fix label sort order --------- Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: Rahul R <rahul.ramesha@plane.so>
This commit is contained in:
parent
be62662bb1
commit
b3ac9def8d
@ -104,6 +104,7 @@ from .estimate import (
|
|||||||
EstimateSerializer,
|
EstimateSerializer,
|
||||||
EstimatePointSerializer,
|
EstimatePointSerializer,
|
||||||
EstimateReadSerializer,
|
EstimateReadSerializer,
|
||||||
|
WorkspaceEstimateSerializer,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .inbox import (
|
from .inbox import (
|
||||||
|
@ -61,3 +61,18 @@ class EstimateReadSerializer(BaseSerializer):
|
|||||||
"name",
|
"name",
|
||||||
"description",
|
"description",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspaceEstimateSerializer(BaseSerializer):
|
||||||
|
points = EstimatePointSerializer(read_only=True, many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Estimate
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = [
|
||||||
|
"points",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,14 +259,17 @@ class IssuePropertySerializer(BaseSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class LabelSerializer(BaseSerializer):
|
class LabelSerializer(BaseSerializer):
|
||||||
workspace_detail = WorkspaceLiteSerializer(
|
|
||||||
source="workspace", read_only=True
|
|
||||||
)
|
|
||||||
project_detail = ProjectLiteSerializer(source="project", read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Label
|
model = Label
|
||||||
fields = "__all__"
|
fields = [
|
||||||
|
"parent",
|
||||||
|
"name",
|
||||||
|
"color",
|
||||||
|
"id",
|
||||||
|
"project_id",
|
||||||
|
"workspace_id",
|
||||||
|
"sort_order",
|
||||||
|
]
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
"workspace",
|
"workspace",
|
||||||
"project",
|
"project",
|
||||||
@ -295,8 +298,12 @@ class IssueLabelSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class IssueRelationSerializer(BaseSerializer):
|
class IssueRelationSerializer(BaseSerializer):
|
||||||
id = serializers.UUIDField(source="related_issue.id", read_only=True)
|
id = serializers.UUIDField(source="related_issue.id", read_only=True)
|
||||||
project_id = serializers.PrimaryKeyRelatedField(source="related_issue.project_id", read_only=True)
|
project_id = serializers.PrimaryKeyRelatedField(
|
||||||
sequence_id = serializers.IntegerField(source="related_issue.sequence_id", read_only=True)
|
source="related_issue.project_id", read_only=True
|
||||||
|
)
|
||||||
|
sequence_id = serializers.IntegerField(
|
||||||
|
source="related_issue.sequence_id", read_only=True
|
||||||
|
)
|
||||||
relation_type = serializers.CharField(read_only=True)
|
relation_type = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -315,8 +322,12 @@ class IssueRelationSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class RelatedIssueSerializer(BaseSerializer):
|
class RelatedIssueSerializer(BaseSerializer):
|
||||||
id = serializers.UUIDField(source="issue.id", read_only=True)
|
id = serializers.UUIDField(source="issue.id", read_only=True)
|
||||||
project_id = serializers.PrimaryKeyRelatedField(source="issue.project_id", read_only=True)
|
project_id = serializers.PrimaryKeyRelatedField(
|
||||||
sequence_id = serializers.IntegerField(source="issue.sequence_id", read_only=True)
|
source="issue.project_id", read_only=True
|
||||||
|
)
|
||||||
|
sequence_id = serializers.IntegerField(
|
||||||
|
source="issue.sequence_id", read_only=True
|
||||||
|
)
|
||||||
relation_type = serializers.CharField(read_only=True)
|
relation_type = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -8,7 +8,17 @@ from plane.db.models import State
|
|||||||
class StateSerializer(BaseSerializer):
|
class StateSerializer(BaseSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = State
|
model = State
|
||||||
fields = "__all__"
|
fields = [
|
||||||
|
"id",
|
||||||
|
"project_id",
|
||||||
|
"workspace_id",
|
||||||
|
"name",
|
||||||
|
"color",
|
||||||
|
"group",
|
||||||
|
"default",
|
||||||
|
"description",
|
||||||
|
"sequence",
|
||||||
|
]
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
"workspace",
|
"workspace",
|
||||||
"project",
|
"project",
|
||||||
|
@ -20,6 +20,8 @@ from plane.app.views import (
|
|||||||
WorkspaceLabelsEndpoint,
|
WorkspaceLabelsEndpoint,
|
||||||
WorkspaceProjectMemberEndpoint,
|
WorkspaceProjectMemberEndpoint,
|
||||||
WorkspaceUserPropertiesEndpoint,
|
WorkspaceUserPropertiesEndpoint,
|
||||||
|
WorkspaceStatesEndpoint,
|
||||||
|
WorkspaceEstimatesEndpoint,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -207,4 +209,14 @@ urlpatterns = [
|
|||||||
WorkspaceUserPropertiesEndpoint.as_view(),
|
WorkspaceUserPropertiesEndpoint.as_view(),
|
||||||
name="workspace-user-filters",
|
name="workspace-user-filters",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/states/",
|
||||||
|
WorkspaceStatesEndpoint.as_view(),
|
||||||
|
name="workspace-state",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"workspaces/<str:slug>/estimates/",
|
||||||
|
WorkspaceEstimatesEndpoint.as_view(),
|
||||||
|
name="workspace-estimate",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -47,6 +47,8 @@ from .workspace import (
|
|||||||
WorkspaceLabelsEndpoint,
|
WorkspaceLabelsEndpoint,
|
||||||
WorkspaceProjectMemberEndpoint,
|
WorkspaceProjectMemberEndpoint,
|
||||||
WorkspaceUserPropertiesEndpoint,
|
WorkspaceUserPropertiesEndpoint,
|
||||||
|
WorkspaceStatesEndpoint,
|
||||||
|
WorkspaceEstimatesEndpoint,
|
||||||
)
|
)
|
||||||
from .state import StateViewSet
|
from .state import StateViewSet
|
||||||
from .view import (
|
from .view import (
|
||||||
|
@ -83,6 +83,7 @@ from plane.utils.grouper import group_results
|
|||||||
from plane.utils.issue_filters import issue_filters
|
from plane.utils.issue_filters import issue_filters
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
class IssueViewSet(WebhookMixin, BaseViewSet):
|
class IssueViewSet(WebhookMixin, BaseViewSet):
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
return (
|
return (
|
||||||
@ -821,7 +822,9 @@ class SubIssuesEndpoint(BaseAPIView):
|
|||||||
|
|
||||||
_ = Issue.objects.bulk_update(sub_issues, ["parent"], batch_size=10)
|
_ = Issue.objects.bulk_update(sub_issues, ["parent"], batch_size=10)
|
||||||
|
|
||||||
updated_sub_issues = Issue.issue_objects.filter(id__in=sub_issue_ids).annotate(state_group=F("state__group"))
|
updated_sub_issues = Issue.issue_objects.filter(
|
||||||
|
id__in=sub_issue_ids
|
||||||
|
).annotate(state_group=F("state__group"))
|
||||||
|
|
||||||
# Track the issue
|
# Track the issue
|
||||||
_ = [
|
_ = [
|
||||||
@ -855,7 +858,6 @@ class SubIssuesEndpoint(BaseAPIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IssueLinkViewSet(BaseViewSet):
|
class IssueLinkViewSet(BaseViewSet):
|
||||||
permission_classes = [
|
permission_classes = [
|
||||||
ProjectEntityPermission,
|
ProjectEntityPermission,
|
||||||
|
@ -9,9 +9,12 @@ from rest_framework.response import Response
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from . import BaseViewSet
|
from . import BaseViewSet, BaseAPIView
|
||||||
from plane.app.serializers import StateSerializer
|
from plane.app.serializers import StateSerializer
|
||||||
from plane.app.permissions import ProjectEntityPermission
|
from plane.app.permissions import (
|
||||||
|
ProjectEntityPermission,
|
||||||
|
WorkspaceEntityPermission,
|
||||||
|
)
|
||||||
from plane.db.models import State, Issue
|
from plane.db.models import State, Issue
|
||||||
|
|
||||||
|
|
||||||
@ -22,9 +25,6 @@ class StateViewSet(BaseViewSet):
|
|||||||
ProjectEntityPermission,
|
ProjectEntityPermission,
|
||||||
]
|
]
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
serializer.save(project_id=self.kwargs.get("project_id"))
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.filter_queryset(
|
return self.filter_queryset(
|
||||||
super()
|
super()
|
||||||
|
@ -46,10 +46,14 @@ from plane.app.serializers import (
|
|||||||
WorkspaceMemberMeSerializer,
|
WorkspaceMemberMeSerializer,
|
||||||
ProjectMemberRoleSerializer,
|
ProjectMemberRoleSerializer,
|
||||||
WorkspaceUserPropertiesSerializer,
|
WorkspaceUserPropertiesSerializer,
|
||||||
|
WorkspaceEstimateSerializer,
|
||||||
|
StateSerializer,
|
||||||
|
LabelSerializer,
|
||||||
)
|
)
|
||||||
from plane.app.views.base import BaseAPIView
|
from plane.app.views.base import BaseAPIView
|
||||||
from . import BaseViewSet
|
from . import BaseViewSet
|
||||||
from plane.db.models import (
|
from plane.db.models import (
|
||||||
|
State,
|
||||||
User,
|
User,
|
||||||
Workspace,
|
Workspace,
|
||||||
WorkspaceMemberInvite,
|
WorkspaceMemberInvite,
|
||||||
@ -67,6 +71,8 @@ from plane.db.models import (
|
|||||||
CycleIssue,
|
CycleIssue,
|
||||||
IssueReaction,
|
IssueReaction,
|
||||||
WorkspaceUserProperties,
|
WorkspaceUserProperties,
|
||||||
|
Estimate,
|
||||||
|
EstimatePoint,
|
||||||
)
|
)
|
||||||
from plane.app.permissions import (
|
from plane.app.permissions import (
|
||||||
WorkSpaceBasePermission,
|
WorkSpaceBasePermission,
|
||||||
@ -1358,7 +1364,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
|
|||||||
.values("count")
|
.values("count")
|
||||||
)
|
)
|
||||||
.annotate(
|
.annotate(
|
||||||
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
|
sub_issues_count=Issue.issue_objects.filter(
|
||||||
|
parent=OuterRef("id")
|
||||||
|
)
|
||||||
.order_by()
|
.order_by()
|
||||||
.annotate(count=Func(F("id"), function="Count"))
|
.annotate(count=Func(F("id"), function="Count"))
|
||||||
.values("count")
|
.values("count")
|
||||||
@ -1441,10 +1449,46 @@ class WorkspaceLabelsEndpoint(BaseAPIView):
|
|||||||
labels = Label.objects.filter(
|
labels = Label.objects.filter(
|
||||||
workspace__slug=slug,
|
workspace__slug=slug,
|
||||||
project__project_projectmember__member=request.user,
|
project__project_projectmember__member=request.user,
|
||||||
).values(
|
|
||||||
"parent", "name", "color", "id", "project_id", "workspace__slug"
|
|
||||||
)
|
)
|
||||||
return Response(labels, status=status.HTTP_200_OK)
|
serializer = LabelSerializer(labels, many=True).data
|
||||||
|
return Response(serializer, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspaceStatesEndpoint(BaseAPIView):
|
||||||
|
permission_classes = [
|
||||||
|
WorkspaceEntityPermission,
|
||||||
|
]
|
||||||
|
|
||||||
|
def get(self, request, slug):
|
||||||
|
states = State.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project__project_projectmember__member=request.user,
|
||||||
|
)
|
||||||
|
serializer = StateSerializer(states, many=True).data
|
||||||
|
return Response(serializer, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspaceEstimatesEndpoint(BaseAPIView):
|
||||||
|
permission_classes = [
|
||||||
|
WorkspaceEntityPermission,
|
||||||
|
]
|
||||||
|
|
||||||
|
def get(self, request, slug):
|
||||||
|
estimate_ids = Project.objects.filter(
|
||||||
|
workspace__slug=slug, estimate__isnull=False
|
||||||
|
).values_list("estimate_id", flat=True)
|
||||||
|
estimates = Estimate.objects.filter(
|
||||||
|
pk__in=estimate_ids
|
||||||
|
).prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"points",
|
||||||
|
queryset=EstimatePoint.objects.select_related(
|
||||||
|
"estimate", "workspace", "project"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
serializer = WorkspaceEstimateSerializer(estimates, many=True)
|
||||||
|
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class WorkspaceUserPropertiesEndpoint(BaseAPIView):
|
class WorkspaceUserPropertiesEndpoint(BaseAPIView):
|
||||||
|
11
packages/types/src/issues.d.ts
vendored
11
packages/types/src/issues.d.ts
vendored
@ -109,17 +109,10 @@ export type IssuePriorities = {
|
|||||||
|
|
||||||
export interface IIssueLabel {
|
export interface IIssueLabel {
|
||||||
id: string;
|
id: string;
|
||||||
created_at: Date;
|
|
||||||
updated_at: Date;
|
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
|
||||||
color: string;
|
color: string;
|
||||||
created_by: string;
|
project_id: string;
|
||||||
updated_by: string;
|
workspace_id: string;
|
||||||
project: string;
|
|
||||||
project_detail: IProjectLite;
|
|
||||||
workspace: string;
|
|
||||||
workspace_detail: IWorkspaceLite;
|
|
||||||
parent: string | null;
|
parent: string | null;
|
||||||
sort_order: number;
|
sort_order: number;
|
||||||
}
|
}
|
||||||
|
11
packages/types/src/state.d.ts
vendored
11
packages/types/src/state.d.ts
vendored
@ -5,20 +5,13 @@ export type TStateGroups = "backlog" | "unstarted" | "started" | "completed" | "
|
|||||||
export interface IState {
|
export interface IState {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
color: string;
|
color: string;
|
||||||
readonly created_at: Date;
|
|
||||||
readonly created_by: string;
|
|
||||||
default: boolean;
|
default: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
group: TStateGroups;
|
group: TStateGroups;
|
||||||
name: string;
|
name: string;
|
||||||
project: string;
|
project_id: string;
|
||||||
readonly project_detail: IProjectLite;
|
|
||||||
sequence: number;
|
sequence: number;
|
||||||
readonly slug: string;
|
workspace_id: string;
|
||||||
readonly updated_at: Date;
|
|
||||||
readonly updated_by: string;
|
|
||||||
workspace: string;
|
|
||||||
workspace_detail: IWorkspaceLite;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStateLite {
|
export interface IStateLite {
|
||||||
|
@ -72,9 +72,7 @@ const UserLink = ({ activity }: { activity: IIssueActivity }) => {
|
|||||||
|
|
||||||
const LabelPill = observer(({ labelId, workspaceSlug }: { labelId: string; workspaceSlug: string }) => {
|
const LabelPill = observer(({ labelId, workspaceSlug }: { labelId: string; workspaceSlug: string }) => {
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { workspaceLabels, fetchWorkspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels, fetchWorkspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!workspaceLabels) fetchWorkspaceLabels(workspaceSlug);
|
if (!workspaceLabels) fetchWorkspaceLabels(workspaceSlug);
|
||||||
|
@ -158,17 +158,12 @@ export const EstimateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
const filteredOptions =
|
const filteredOptions =
|
||||||
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
|
||||||
// fetch cycles of the project if not already present in the store
|
const selectedEstimate = value !== null ? getEstimatePointValue(value, projectId) : null;
|
||||||
useEffect(() => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
|
|
||||||
if (!activeEstimate) fetchProjectEstimates(workspaceSlug, projectId);
|
|
||||||
}, [activeEstimate, fetchProjectEstimates, projectId, workspaceSlug]);
|
|
||||||
|
|
||||||
const selectedEstimate = value !== null ? getEstimatePointValue(value) : null;
|
|
||||||
|
|
||||||
const openDropdown = () => {
|
const openDropdown = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
|
||||||
|
if (!activeEstimate && workspaceSlug) fetchProjectEstimates(workspaceSlug, projectId);
|
||||||
if (referenceElement) referenceElement.focus();
|
if (referenceElement) referenceElement.focus();
|
||||||
};
|
};
|
||||||
const closeDropdown = () => setIsOpen(false);
|
const closeDropdown = () => setIsOpen(false);
|
||||||
|
@ -93,14 +93,10 @@ export const ProjectMemberDropdown: React.FC<Props> = observer((props) => {
|
|||||||
};
|
};
|
||||||
if (multiple) comboboxProps.multiple = true;
|
if (multiple) comboboxProps.multiple = true;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
|
|
||||||
if (!projectMemberIds) fetchProjectMembers(workspaceSlug, projectId);
|
|
||||||
}, [fetchProjectMembers, projectId, projectMemberIds, workspaceSlug]);
|
|
||||||
|
|
||||||
const openDropdown = () => {
|
const openDropdown = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
|
||||||
|
if (!projectMemberIds && workspaceSlug) fetchProjectMembers(workspaceSlug, projectId);
|
||||||
if (referenceElement) referenceElement.focus();
|
if (referenceElement) referenceElement.focus();
|
||||||
};
|
};
|
||||||
const closeDropdown = () => setIsOpen(false);
|
const closeDropdown = () => setIsOpen(false);
|
||||||
|
@ -142,17 +142,12 @@ export const StateDropdown: React.FC<Props> = observer((props) => {
|
|||||||
const filteredOptions =
|
const filteredOptions =
|
||||||
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
|
||||||
// fetch states of the project if not already present in the store
|
|
||||||
useEffect(() => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
|
|
||||||
if (!statesList) fetchProjectStates(workspaceSlug, projectId);
|
|
||||||
}, [fetchProjectStates, projectId, statesList, workspaceSlug]);
|
|
||||||
|
|
||||||
const selectedState = getStateById(value);
|
const selectedState = getStateById(value);
|
||||||
|
|
||||||
const openDropdown = () => {
|
const openDropdown = () => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
if (!statesList && workspaceSlug) fetchProjectStates(workspaceSlug, projectId);
|
||||||
if (referenceElement) referenceElement.focus();
|
if (referenceElement) referenceElement.focus();
|
||||||
};
|
};
|
||||||
const closeDropdown = () => setIsOpen(false);
|
const closeDropdown = () => setIsOpen(false);
|
||||||
|
@ -75,9 +75,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
} = useUser();
|
} = useUser();
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -45,9 +45,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const {
|
const { workspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
workspace: { workspaceMemberIds },
|
workspace: { workspaceMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -77,9 +77,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
|
@ -25,9 +25,7 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => {
|
|||||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -22,9 +22,7 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
|||||||
} = useIssues(EIssuesStoreType.DRAFT);
|
} = useIssues(EIssuesStoreType.DRAFT);
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -42,9 +42,7 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
|
|||||||
} = useUser();
|
} = useUser();
|
||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
|
|
||||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||||
|
|
||||||
|
@ -49,9 +49,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
|||||||
const { currentProjectDetails } = useProject();
|
const { currentProjectDetails } = useProject();
|
||||||
const { projectViewIds, getViewById } = useProjectView();
|
const { projectViewIds, getViewById } = useProjectView();
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -24,9 +24,7 @@ export const IssueLabel: FC<TIssueLabel> = observer((props) => {
|
|||||||
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
||||||
// hooks
|
// hooks
|
||||||
const { updateIssue } = useIssueDetail();
|
const { updateIssue } = useIssueDetail();
|
||||||
const {
|
const { createLabel } = useLabel();
|
||||||
project: { createLabel },
|
|
||||||
} = useLabel();
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const labelOperations: TLabelOperations = useMemo(
|
const labelOperations: TLabelOperations = useMemo(
|
||||||
|
@ -20,9 +20,7 @@ export const IssueLabelSelect: React.FC<IIssueLabelSelect> = observer((props) =>
|
|||||||
const {
|
const {
|
||||||
issue: { getIssueById },
|
issue: { getIssueById },
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
const {
|
const { fetchProjectLabels, getProjectLabels } = useLabel();
|
||||||
project: { fetchProjectLabels, projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
// states
|
// states
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||||
@ -30,10 +28,12 @@ export const IssueLabelSelect: React.FC<IIssueLabelSelect> = observer((props) =>
|
|||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
const issue = getIssueById(issueId);
|
const issue = getIssueById(issueId);
|
||||||
|
const projectLabels = getProjectLabels(projectId);
|
||||||
|
|
||||||
const fetchLabels = () => {
|
const fetchLabels = () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (workspaceSlug && projectId) fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
if (!projectLabels && workspaceSlug && projectId)
|
||||||
|
fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = (projectLabels ?? []).map((label) => ({
|
const options = (projectLabels ?? []).map((label) => ({
|
||||||
|
@ -17,9 +17,7 @@ export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
// derived values
|
// derived values
|
||||||
const userFilters = issueFilters?.filters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
@ -21,9 +21,7 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.CYCLE);
|
} = useIssues(EIssuesStoreType.CYCLE);
|
||||||
|
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
// derived values
|
// derived values
|
||||||
const userFilters = issueFilters?.filters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
@ -16,9 +16,7 @@ export const DraftIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.DRAFT);
|
} = useIssues(EIssuesStoreType.DRAFT);
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
// derived values
|
// derived values
|
||||||
const userFilters = issueFilters?.filters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
@ -25,9 +25,7 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||||||
const {
|
const {
|
||||||
issuesFilter: { filters, updateFilters },
|
issuesFilter: { filters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||||
const {
|
const { workspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { globalViewMap, updateGlobalView } = useGlobalView();
|
const { globalViewMap, updateGlobalView } = useGlobalView();
|
||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
|
@ -20,9 +20,7 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.MODULE);
|
} = useIssues(EIssuesStoreType.MODULE);
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
// derived values
|
// derived values
|
||||||
const userFilters = issueFilters?.filters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
@ -7,19 +7,20 @@ import { AppliedFiltersList } from "components/issues";
|
|||||||
// types
|
// types
|
||||||
import { IIssueFilterOptions } from "@plane/types";
|
import { IIssueFilterOptions } from "@plane/types";
|
||||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||||
|
import { useWorskspaceIssueProperties } from "hooks/use-worskspace-issue-properties";
|
||||||
|
|
||||||
export const ProfileIssuesAppliedFiltersRoot: React.FC = observer(() => {
|
export const ProfileIssuesAppliedFiltersRoot: React.FC = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, userId } = router.query;
|
const { workspaceSlug, userId } = router.query;
|
||||||
|
//swr hook for fetching issue properties
|
||||||
|
useWorskspaceIssueProperties(workspaceSlug);
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.PROFILE);
|
} = useIssues(EIssuesStoreType.PROFILE);
|
||||||
|
|
||||||
const {
|
const { workspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
// derived values
|
// derived values
|
||||||
const userFilters = issueFilters?.filters;
|
const userFilters = issueFilters?.filters;
|
||||||
|
|
||||||
|
@ -19,9 +19,7 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
};
|
};
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.PROJECT);
|
} = useIssues(EIssuesStoreType.PROJECT);
|
||||||
|
@ -23,9 +23,7 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
} = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const { viewMap, updateView } = useProjectView();
|
const { viewMap, updateView } = useProjectView();
|
||||||
// derived values
|
// derived values
|
||||||
|
@ -71,10 +71,10 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
|
|
||||||
const member = useMember();
|
const member = useMember();
|
||||||
const project = useProject();
|
const project = useProject();
|
||||||
const projectLabel = useLabel();
|
const label = useLabel();
|
||||||
const projectState = useProjectState();
|
const projectState = useProjectState();
|
||||||
|
|
||||||
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, projectLabel, projectState, member);
|
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member);
|
||||||
|
|
||||||
if (!list) return null;
|
if (!list) return null;
|
||||||
|
|
||||||
|
@ -208,17 +208,11 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
|||||||
|
|
||||||
const member = useMember();
|
const member = useMember();
|
||||||
const project = useProject();
|
const project = useProject();
|
||||||
const projectLabel = useLabel();
|
const label = useLabel();
|
||||||
const projectState = useProjectState();
|
const projectState = useProjectState();
|
||||||
|
|
||||||
const groupByList = getGroupByColumns(group_by as GroupByColumnTypes, project, projectLabel, projectState, member);
|
const groupByList = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member);
|
||||||
const subGroupByList = getGroupByColumns(
|
const subGroupByList = getGroupByColumns(sub_group_by as GroupByColumnTypes, project, label, projectState, member);
|
||||||
sub_group_by as GroupByColumnTypes,
|
|
||||||
project,
|
|
||||||
projectLabel,
|
|
||||||
projectState,
|
|
||||||
member
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!groupByList || !subGroupByList) return null;
|
if (!groupByList || !subGroupByList) return null;
|
||||||
|
|
||||||
|
@ -59,10 +59,10 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const member = useMember();
|
const member = useMember();
|
||||||
const project = useProject();
|
const project = useProject();
|
||||||
const projectLabel = useLabel();
|
const label = useLabel();
|
||||||
const projectState = useProjectState();
|
const projectState = useProjectState();
|
||||||
|
|
||||||
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, projectLabel, projectState, member, true);
|
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member, true);
|
||||||
|
|
||||||
if (!list) return null;
|
if (!list) return null;
|
||||||
|
|
||||||
|
@ -53,13 +53,15 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
const {
|
const {
|
||||||
router: { workspaceSlug },
|
router: { workspaceSlug },
|
||||||
} = useApplication();
|
} = useApplication();
|
||||||
const {
|
const { fetchProjectLabels, getProjectLabels } = useLabel();
|
||||||
project: { fetchProjectLabels, projectLabels: storeLabels },
|
|
||||||
} = useLabel();
|
|
||||||
|
|
||||||
const fetchLabels = () => {
|
const storeLabels = getProjectLabels(projectId);
|
||||||
|
|
||||||
|
const openDropDown = () => {
|
||||||
|
if (!storeLabels && workspaceSlug && projectId) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (workspaceSlug && projectId) fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
fetchProjectLabels(workspaceSlug, projectId).then(() => setIsLoading(false));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||||
@ -182,7 +184,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
|
|||||||
? "cursor-pointer"
|
? "cursor-pointer"
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
} ${buttonClassName}`}
|
} ${buttonClassName}`}
|
||||||
onClick={() => !storeLabels && fetchLabels()}
|
onClick={openDropDown}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
{!hideDropdownArrow && !disabled && <ChevronDown className="h-3 w-3" aria-hidden="true" />}
|
||||||
|
@ -4,6 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// hooks
|
// hooks
|
||||||
import { useGlobalView, useIssues, useUser } from "hooks/store";
|
import { useGlobalView, useIssues, useUser } from "hooks/store";
|
||||||
|
import { useWorskspaceIssueProperties } from "hooks/use-worskspace-issue-properties";
|
||||||
// components
|
// components
|
||||||
import { GlobalViewsAppliedFiltersRoot, IssuePeekOverview } from "components/issues";
|
import { GlobalViewsAppliedFiltersRoot, IssuePeekOverview } from "components/issues";
|
||||||
import { SpreadsheetView } from "components/issues/issue-layouts";
|
import { SpreadsheetView } from "components/issues/issue-layouts";
|
||||||
@ -20,7 +21,8 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
|||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, globalViewId } = router.query;
|
const { workspaceSlug, globalViewId } = router.query;
|
||||||
|
//swr hook for fetching issue properties
|
||||||
|
useWorskspaceIssueProperties(workspaceSlug);
|
||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
issuesFilter: { filters, fetchFilters, updateFilters },
|
issuesFilter: { filters, fetchFilters, updateFilters },
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
|
||||||
import { ISSUE_PRIORITIES } from "constants/issue";
|
import { ISSUE_PRIORITIES } from "constants/issue";
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
import { ILabelRootStore } from "store/label";
|
|
||||||
import { IMemberRootStore } from "store/member";
|
import { IMemberRootStore } from "store/member";
|
||||||
import { IProjectStore } from "store/project/project.store";
|
import { IProjectStore } from "store/project/project.store";
|
||||||
import { IStateStore } from "store/state.store";
|
import { IStateStore } from "store/state.store";
|
||||||
import { GroupByColumnTypes, IGroupByColumn } from "@plane/types";
|
import { GroupByColumnTypes, IGroupByColumn } from "@plane/types";
|
||||||
import { STATE_GROUPS } from "constants/state";
|
import { STATE_GROUPS } from "constants/state";
|
||||||
|
import { ILabelStore } from "store/label.store";
|
||||||
|
|
||||||
export const getGroupByColumns = (
|
export const getGroupByColumns = (
|
||||||
groupBy: GroupByColumnTypes | null,
|
groupBy: GroupByColumnTypes | null,
|
||||||
project: IProjectStore,
|
project: IProjectStore,
|
||||||
projectLabel: ILabelRootStore,
|
label: ILabelStore,
|
||||||
projectState: IStateStore,
|
projectState: IStateStore,
|
||||||
member: IMemberRootStore,
|
member: IMemberRootStore,
|
||||||
includeNone?: boolean
|
includeNone?: boolean
|
||||||
@ -26,7 +26,7 @@ export const getGroupByColumns = (
|
|||||||
case "priority":
|
case "priority":
|
||||||
return getPriorityColumns();
|
return getPriorityColumns();
|
||||||
case "labels":
|
case "labels":
|
||||||
return getLabelsColumns(projectLabel) as any;
|
return getLabelsColumns(label) as any;
|
||||||
case "assignees":
|
case "assignees":
|
||||||
return getAssigneeColumns(member) as any;
|
return getAssigneeColumns(member) as any;
|
||||||
case "created_by":
|
case "created_by":
|
||||||
@ -97,10 +97,8 @@ const getPriorityColumns = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLabelsColumns = (projectLabel: ILabelRootStore) => {
|
const getLabelsColumns = (label: ILabelStore) => {
|
||||||
const {
|
const { projectLabels } = label;
|
||||||
project: { projectLabels },
|
|
||||||
} = projectLabel;
|
|
||||||
|
|
||||||
if (!projectLabels) return;
|
if (!projectLabels) return;
|
||||||
|
|
||||||
|
@ -64,7 +64,15 @@ const aiService = new AIService();
|
|||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||||
const { data, onChange, onClose, onSubmit, projectId, isCreateMoreToggleEnabled, onCreateMoreToggleChange } = props;
|
const {
|
||||||
|
data,
|
||||||
|
onChange,
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
projectId: defaultProjectId,
|
||||||
|
isCreateMoreToggleEnabled,
|
||||||
|
onCreateMoreToggleChange,
|
||||||
|
} = props;
|
||||||
// states
|
// states
|
||||||
const [labelModal, setLabelModal] = useState(false);
|
const [labelModal, setLabelModal] = useState(false);
|
||||||
const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false);
|
const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false);
|
||||||
@ -99,10 +107,30 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
getValues,
|
getValues,
|
||||||
setValue,
|
setValue,
|
||||||
} = useForm<TIssue>({
|
} = useForm<TIssue>({
|
||||||
defaultValues: { ...defaultValues, project_id: projectId, ...data },
|
defaultValues: { ...defaultValues, project_id: defaultProjectId, ...data },
|
||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const projectId = watch("project_id");
|
||||||
|
|
||||||
|
//reset few fields on projectId change
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDirty) {
|
||||||
|
const formData = getValues();
|
||||||
|
|
||||||
|
reset({
|
||||||
|
...defaultValues,
|
||||||
|
project_id: projectId,
|
||||||
|
name: formData.name,
|
||||||
|
description_html: formData.description_html,
|
||||||
|
priority: formData.priority,
|
||||||
|
start_date: formData.start_date,
|
||||||
|
target_date: formData.target_date,
|
||||||
|
parent_id: formData.parent_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [projectId]);
|
||||||
|
|
||||||
const issueName = watch("name");
|
const issueName = watch("name");
|
||||||
|
|
||||||
const handleFormSubmit = async (formData: Partial<TIssue>) => {
|
const handleFormSubmit = async (formData: Partial<TIssue>) => {
|
||||||
@ -130,7 +158,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
setIAmFeelingLucky(true);
|
setIAmFeelingLucky(true);
|
||||||
|
|
||||||
aiService
|
aiService
|
||||||
.createGptTask(workspaceSlug.toString(), projectId.toString(), {
|
.createGptTask(workspaceSlug.toString(), projectId, {
|
||||||
prompt: issueName,
|
prompt: issueName,
|
||||||
task: "Generate a proper description for this issue.",
|
task: "Generate a proper description for this issue.",
|
||||||
})
|
})
|
||||||
|
@ -23,14 +23,7 @@ export interface IssuesModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((props) => {
|
export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((props) => {
|
||||||
const {
|
const { data, isOpen, onClose, onSubmit, withDraftIssueWrapper = true, storeType = EIssuesStoreType.PROJECT } = props;
|
||||||
data,
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSubmit,
|
|
||||||
withDraftIssueWrapper = true,
|
|
||||||
storeType = EIssuesStoreType.PROJECT,
|
|
||||||
} = props;
|
|
||||||
// states
|
// states
|
||||||
const [changesMade, setChangesMade] = useState<Partial<TIssue> | null>(null);
|
const [changesMade, setChangesMade] = useState<Partial<TIssue> | null>(null);
|
||||||
const [createMore, setCreateMore] = useState(false);
|
const [createMore, setCreateMore] = useState(false);
|
||||||
|
@ -29,9 +29,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { getProjectLabels, fetchProjectLabels } = useLabel();
|
||||||
project: { getProjectLabels, fetchProjectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
// states
|
// states
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||||
@ -50,13 +48,9 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
|
|||||||
const filteredOptions =
|
const filteredOptions =
|
||||||
query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
|
query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
|
||||||
useSWR(
|
|
||||||
workspaceSlug && projectId ? `PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}` : null,
|
|
||||||
workspaceSlug && projectId ? () => fetchProjectLabels(workspaceSlug.toString(), projectId) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
const openDropdown = () => {
|
const openDropdown = () => {
|
||||||
setIsDropdownOpen(true);
|
setIsDropdownOpen(true);
|
||||||
|
if (!projectLabels && workspaceSlug && projectId) fetchProjectLabels(workspaceSlug.toString(), projectId);
|
||||||
if (referenceElement) referenceElement.focus();
|
if (referenceElement) referenceElement.focus();
|
||||||
};
|
};
|
||||||
const closeDropdown = () => setIsDropdownOpen(false);
|
const closeDropdown = () => setIsDropdownOpen(false);
|
||||||
|
@ -21,7 +21,7 @@ export const ViewEstimateSelect: React.FC<Props> = observer((props) => {
|
|||||||
const { issue, onChange, tooltipPosition = "top", customButton = false, disabled } = props;
|
const { issue, onChange, tooltipPosition = "top", customButton = false, disabled } = props;
|
||||||
const { areEstimatesEnabledForCurrentProject, activeEstimateDetails, getEstimatePointValue } = useEstimate();
|
const { areEstimatesEnabledForCurrentProject, activeEstimateDetails, getEstimatePointValue } = useEstimate();
|
||||||
|
|
||||||
const estimateValue = getEstimatePointValue(issue.estimate_point);
|
const estimateValue = getEstimatePointValue(issue.estimate_point, issue.project_id);
|
||||||
|
|
||||||
const estimateLabels = (
|
const estimateLabels = (
|
||||||
<Tooltip tooltipHeading="Estimate" tooltipContent={estimateValue} position={tooltipPosition}>
|
<Tooltip tooltipHeading="Estimate" tooltipContent={estimateValue} position={tooltipPosition}>
|
||||||
|
@ -34,9 +34,7 @@ export const CreateLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { createLabel } = useLabel();
|
||||||
project: { createLabel },
|
|
||||||
} = useLabel();
|
|
||||||
// form info
|
// form info
|
||||||
const {
|
const {
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
|
@ -34,9 +34,7 @@ export const CreateUpdateLabelInline = observer(
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { createLabel, updateLabel } = useLabel();
|
||||||
project: { createLabel, updateLabel },
|
|
||||||
} = useLabel();
|
|
||||||
// toast alert
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
// form info
|
// form info
|
||||||
|
@ -25,9 +25,7 @@ export const DeleteLabelModal: React.FC<Props> = observer((props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { deleteLabel } = useLabel();
|
||||||
project: { deleteLabel },
|
|
||||||
} = useLabel();
|
|
||||||
// states
|
// states
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
// hooks
|
// hooks
|
||||||
|
@ -25,9 +25,7 @@ export const LabelsListModal: React.FC<Props> = observer((props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { projectLabels, fetchProjectLabels, updateLabel } = useLabel();
|
||||||
project: { projectLabels, fetchProjectLabels, updateLabel },
|
|
||||||
} = useLabel();
|
|
||||||
|
|
||||||
// api call to fetch project details
|
// api call to fetch project details
|
||||||
useSWR(
|
useSWR(
|
||||||
|
@ -28,9 +28,7 @@ export const ProjectSettingLabelItem: React.FC<Props> = (props) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { updateLabel } = useLabel();
|
||||||
project: { updateLabel },
|
|
||||||
} = useLabel();
|
|
||||||
|
|
||||||
const removeFromGroup = (label: IIssueLabel) => {
|
const removeFromGroup = (label: IIssueLabel) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
@ -41,9 +41,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { projectLabels, updateLabelPosition, projectLabelsTree } = useLabel();
|
||||||
project: { projectLabels, updateLabelPosition, projectLabelsTree },
|
|
||||||
} = useLabel();
|
|
||||||
// portal
|
// portal
|
||||||
const renderDraggable = useDraggableInPortal();
|
const renderDraggable = useDraggableInPortal();
|
||||||
|
|
||||||
|
@ -18,9 +18,7 @@ export const ProfileIssuesFilter = observer(() => {
|
|||||||
issuesFilter: { issueFilters, updateFilters },
|
issuesFilter: { issueFilters, updateFilters },
|
||||||
} = useIssues(EIssuesStoreType.PROFILE);
|
} = useIssues(EIssuesStoreType.PROFILE);
|
||||||
|
|
||||||
const {
|
const { workspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
// derived values
|
// derived values
|
||||||
const states = undefined;
|
const states = undefined;
|
||||||
const members = undefined;
|
const members = undefined;
|
||||||
|
@ -42,7 +42,7 @@ export const DeleteStateModal: React.FC<Props> = observer((props) => {
|
|||||||
|
|
||||||
setIsDeleteLoading(true);
|
setIsDeleteLoading(true);
|
||||||
|
|
||||||
await deleteState(workspaceSlug.toString(), data.project, data.id)
|
await deleteState(workspaceSlug.toString(), data.project_id, data.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
postHogEventTracker("STATE_DELETE", {
|
postHogEventTracker("STATE_DELETE", {
|
||||||
state: "SUCCESS",
|
state: "SUCCESS",
|
||||||
|
@ -28,9 +28,7 @@ export const ProjectViewForm: React.FC<Props> = observer((props) => {
|
|||||||
const { handleFormSubmit, handleClose, data, preLoadedData } = props;
|
const { handleFormSubmit, handleClose, data, preLoadedData } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { projectStates } = useProjectState();
|
const { projectStates } = useProjectState();
|
||||||
const {
|
const { projectLabels } = useLabel();
|
||||||
project: { projectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
project: { projectMemberIds },
|
project: { projectMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -27,9 +27,7 @@ const defaultValues: Partial<IWorkspaceView> = {
|
|||||||
export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
export const WorkspaceViewForm: React.FC<Props> = observer((props) => {
|
||||||
const { handleFormSubmit, handleClose, data, preLoadedData } = props;
|
const { handleFormSubmit, handleClose, data, preLoadedData } = props;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const { workspaceLabels } = useLabel();
|
||||||
workspace: { workspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const {
|
const {
|
||||||
workspace: { workspaceMemberIds },
|
workspace: { workspaceMemberIds },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
@ -2,10 +2,10 @@ import { useContext } from "react";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { StoreContext } from "contexts/store-context";
|
import { StoreContext } from "contexts/store-context";
|
||||||
// types
|
// types
|
||||||
import { ILabelRootStore } from "store/label";
|
import { ILabelStore } from "store/label.store";
|
||||||
|
|
||||||
export const useLabel = (): ILabelRootStore => {
|
export const useLabel = (): ILabelStore => {
|
||||||
const context = useContext(StoreContext);
|
const context = useContext(StoreContext);
|
||||||
if (context === undefined) throw new Error("useLabel must be used within StoreProvider");
|
if (context === undefined) throw new Error("useLabel must be used within StoreProvider");
|
||||||
return context.labelRoot;
|
return context.label;
|
||||||
};
|
};
|
||||||
|
28
web/hooks/use-worskspace-issue-properties.ts
Normal file
28
web/hooks/use-worskspace-issue-properties.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import { useEstimate, useLabel, useProjectState } from "./store";
|
||||||
|
|
||||||
|
export const useWorskspaceIssueProperties = (workspaceSlug: string | string[] | undefined) => {
|
||||||
|
const { fetchWorkspaceLabels } = useLabel();
|
||||||
|
|
||||||
|
const { fetchWorkspaceStates } = useProjectState();
|
||||||
|
|
||||||
|
const { fetchWorskpaceEstimates } = useEstimate();
|
||||||
|
|
||||||
|
// fetch workspace labels
|
||||||
|
useSWR(
|
||||||
|
workspaceSlug ? `WORKSPACE_LABELS_${workspaceSlug}` : null,
|
||||||
|
workspaceSlug ? () => fetchWorkspaceLabels(workspaceSlug.toString()) : null
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetch workspace states
|
||||||
|
useSWR(
|
||||||
|
workspaceSlug ? `WORKSPACE_STATES_${workspaceSlug}` : null,
|
||||||
|
workspaceSlug ? () => fetchWorkspaceStates(workspaceSlug.toString()) : null
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetch workspace estimates
|
||||||
|
useSWR(
|
||||||
|
workspaceSlug ? `WORKSPACE_ESTIMATES_${workspaceSlug}` : null,
|
||||||
|
workspaceSlug ? () => fetchWorskpaceEstimates(workspaceSlug.toString()) : null
|
||||||
|
);
|
||||||
|
};
|
@ -45,9 +45,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||||||
project: { fetchProjectMembers },
|
project: { fetchProjectMembers },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
const { fetchProjectStates } = useProjectState();
|
const { fetchProjectStates } = useProjectState();
|
||||||
const {
|
const { fetchProjectLabels } = useLabel();
|
||||||
project: { fetchProjectLabels },
|
|
||||||
} = useLabel();
|
|
||||||
const { fetchProjectEstimates } = useEstimate();
|
const { fetchProjectEstimates } = useEstimate();
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -20,9 +20,6 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||||||
const {
|
const {
|
||||||
workspace: { fetchWorkspaceMembers },
|
workspace: { fetchWorkspaceMembers },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
const {
|
|
||||||
workspace: { fetchWorkspaceLabels },
|
|
||||||
} = useLabel();
|
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
@ -41,11 +38,6 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||||||
workspaceSlug ? `WORKSPACE_MEMBERS_${workspaceSlug}` : null,
|
workspaceSlug ? `WORKSPACE_MEMBERS_${workspaceSlug}` : null,
|
||||||
workspaceSlug ? () => fetchWorkspaceMembers(workspaceSlug.toString()) : null
|
workspaceSlug ? () => fetchWorkspaceMembers(workspaceSlug.toString()) : null
|
||||||
);
|
);
|
||||||
// fetch workspace labels
|
|
||||||
useSWR(
|
|
||||||
workspaceSlug ? `WORKSPACE_LABELS_${workspaceSlug}` : null,
|
|
||||||
workspaceSlug ? () => fetchWorkspaceLabels(workspaceSlug.toString()) : null
|
|
||||||
);
|
|
||||||
// fetch workspace user projects role
|
// fetch workspace user projects role
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug ? `WORKSPACE_PROJECTS_ROLE_${workspaceSlug}` : null,
|
workspaceSlug ? `WORKSPACE_PROJECTS_ROLE_${workspaceSlug}` : null,
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"lucide-react": "^0.294.0",
|
"lucide-react": "^0.294.0",
|
||||||
"mobx": "^6.10.0",
|
"mobx": "^6.10.0",
|
||||||
"mobx-react": "^9.1.0",
|
"mobx-react": "^9.1.0",
|
||||||
|
"mobx-utils": "^6.0.8",
|
||||||
"next": "^14.0.3",
|
"next": "^14.0.3",
|
||||||
"next-pwa": "^5.6.0",
|
"next-pwa": "^5.6.0",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
|
@ -61,4 +61,12 @@ export class ProjectEstimateService extends APIService {
|
|||||||
throw error?.response?.data;
|
throw error?.response?.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getWorkspaceEstimatesList(workspaceSlug: string): Promise<IEstimate[]> {
|
||||||
|
return this.get(`/api/workspaces/${workspaceSlug}/estimates/`)
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,4 +65,12 @@ export class ProjectStateService extends APIService {
|
|||||||
throw error?.response;
|
throw error?.response;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getWorkspaceStates(workspaceSlug: string): Promise<IState[]> {
|
||||||
|
return this.get(`/api/workspaces/${workspaceSlug}/states/`)
|
||||||
|
.then((response) => response?.data)
|
||||||
|
.catch((error) => {
|
||||||
|
throw error?.response?.data;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class EventTrackerStore implements IEventTrackerStore {
|
|||||||
constructor(_rootStore: RootStore) {
|
constructor(_rootStore: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
trackElement: observable,
|
trackElement: observable,
|
||||||
setTrackElement: action,
|
setTrackElement: action.bound,
|
||||||
postHogEventTracker: action,
|
postHogEventTracker: action,
|
||||||
});
|
});
|
||||||
this.rootStore = _rootStore;
|
this.rootStore = _rootStore;
|
||||||
|
@ -29,7 +29,7 @@ export class RouterStore implements IRouterStore {
|
|||||||
// observables
|
// observables
|
||||||
query: observable,
|
query: observable,
|
||||||
// actions
|
// actions
|
||||||
setQuery: action,
|
setQuery: action.bound,
|
||||||
//computed
|
//computed
|
||||||
workspaceSlug: computed,
|
workspaceSlug: computed,
|
||||||
projectId: computed,
|
projectId: computed,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { isFuture, isPast } from "date-fns";
|
import { isFuture, isPast } from "date-fns";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
@ -74,10 +75,6 @@ export class CycleStore implements ICycleStore {
|
|||||||
currentProjectIncompleteCycleIds: computed,
|
currentProjectIncompleteCycleIds: computed,
|
||||||
currentProjectDraftCycleIds: computed,
|
currentProjectDraftCycleIds: computed,
|
||||||
currentProjectActiveCycleId: computed,
|
currentProjectActiveCycleId: computed,
|
||||||
// computed actions
|
|
||||||
getCycleById: action,
|
|
||||||
getActiveCycleById: action,
|
|
||||||
getProjectCycleIds: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchAllCycles: action,
|
fetchAllCycles: action,
|
||||||
fetchActiveCycle: action,
|
fetchActiveCycle: action,
|
||||||
@ -184,28 +181,29 @@ export class CycleStore implements ICycleStore {
|
|||||||
* @param cycleId
|
* @param cycleId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getCycleById = (cycleId: string): ICycle | null => this.cycleMap?.[cycleId] ?? null;
|
getCycleById = computedFn((cycleId: string): ICycle | null => this.cycleMap?.[cycleId] ?? null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns active cycle details by cycle id
|
* @description returns active cycle details by cycle id
|
||||||
* @param cycleId
|
* @param cycleId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getActiveCycleById = (cycleId: string): ICycle | null =>
|
getActiveCycleById = computedFn((cycleId: string): ICycle | null =>
|
||||||
this.activeCycleIdMap?.[cycleId] && this.cycleMap?.[cycleId] ? this.cycleMap?.[cycleId] : null;
|
this.activeCycleIdMap?.[cycleId] && this.cycleMap?.[cycleId] ? this.cycleMap?.[cycleId] : null
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns list of cycle ids of the project id passed as argument
|
* @description returns list of cycle ids of the project id passed as argument
|
||||||
* @param projectId
|
* @param projectId
|
||||||
*/
|
*/
|
||||||
getProjectCycleIds = (projectId: string): string[] | null => {
|
getProjectCycleIds = computedFn((projectId: string): string[] | null => {
|
||||||
if (!this.fetchedMap[projectId]) return null;
|
if (!this.fetchedMap[projectId]) return null;
|
||||||
|
|
||||||
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
|
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
|
||||||
cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
|
cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
|
||||||
const cycleIds = cycles.map((c) => c.id);
|
const cycleIds = cycles.map((c) => c.id);
|
||||||
return cycleIds || null;
|
return cycleIds || null;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description validates cycle dates
|
* @description validates cycle dates
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
// services
|
// services
|
||||||
import { DashboardService } from "services/dashboard.service";
|
import { DashboardService } from "services/dashboard.service";
|
||||||
@ -74,8 +75,6 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
widgetStats: observable,
|
widgetStats: observable,
|
||||||
// computed
|
// computed
|
||||||
homeDashboardWidgets: computed,
|
homeDashboardWidgets: computed,
|
||||||
// computed actions
|
|
||||||
getWidgetDetails: action,
|
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchHomeDashboardWidgets: action,
|
fetchHomeDashboardWidgets: action,
|
||||||
fetchWidgetStats: action,
|
fetchWidgetStats: action,
|
||||||
@ -109,11 +108,11 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
* @param widgetId
|
* @param widgetId
|
||||||
* @returns widget details
|
* @returns widget details
|
||||||
*/
|
*/
|
||||||
getWidgetDetails = (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => {
|
getWidgetDetails = computedFn((workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => {
|
||||||
const widgets = this.widgetDetails?.[workspaceSlug]?.[dashboardId];
|
const widgets = this.widgetDetails?.[workspaceSlug]?.[dashboardId];
|
||||||
if (!widgets) return undefined;
|
if (!widgets) return undefined;
|
||||||
return widgets.find((widget) => widget.key === widgetKey);
|
return widgets.find((widget) => widget.key === widgetKey);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch home dashboard details and widgets
|
* @description fetch home dashboard details and widgets
|
||||||
|
@ -5,21 +5,25 @@ import { ProjectEstimateService } from "services/project";
|
|||||||
// types
|
// types
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
import { IEstimate, IEstimateFormData } from "@plane/types";
|
import { IEstimate, IEstimateFormData } from "@plane/types";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
|
|
||||||
export interface IEstimateStore {
|
export interface IEstimateStore {
|
||||||
|
//Loaders
|
||||||
|
fetchedMap: Record<string, boolean>;
|
||||||
// observables
|
// observables
|
||||||
estimates: Record<string, IEstimate[] | null>;
|
estimateMap: Record<string, IEstimate>;
|
||||||
// computed
|
// computed
|
||||||
areEstimatesEnabledForCurrentProject: boolean;
|
areEstimatesEnabledForCurrentProject: boolean;
|
||||||
projectEstimates: IEstimate[] | null;
|
projectEstimates: IEstimate[] | null;
|
||||||
activeEstimateDetails: IEstimate | null;
|
activeEstimateDetails: IEstimate | null;
|
||||||
// computed actions
|
// computed actions
|
||||||
areEstimatesEnabledForProject: (projectId: string) => boolean;
|
areEstimatesEnabledForProject: (projectId: string) => boolean;
|
||||||
getEstimatePointValue: (estimateKey: number | null) => string;
|
getEstimatePointValue: (estimateKey: number | null, projectId?: string) => string;
|
||||||
getProjectEstimateById: (estimateId: string) => IEstimate | null;
|
getProjectEstimateById: (estimateId: string) => IEstimate | null;
|
||||||
getProjectActiveEstimateDetails: (projectId: string) => IEstimate | null;
|
getProjectActiveEstimateDetails: (projectId: string) => IEstimate | null;
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchProjectEstimates: (workspaceSlug: string, projectId: string) => Promise<IEstimate[]>;
|
fetchProjectEstimates: (workspaceSlug: string, projectId: string) => Promise<IEstimate[]>;
|
||||||
|
fetchWorskpaceEstimates: (workspaceSlug: string) => Promise<IEstimate[]>;
|
||||||
// crud actions
|
// crud actions
|
||||||
createEstimate: (workspaceSlug: string, projectId: string, data: IEstimateFormData) => Promise<IEstimate>;
|
createEstimate: (workspaceSlug: string, projectId: string, data: IEstimateFormData) => Promise<IEstimate>;
|
||||||
updateEstimate: (
|
updateEstimate: (
|
||||||
@ -33,7 +37,9 @@ export interface IEstimateStore {
|
|||||||
|
|
||||||
export class EstimateStore implements IEstimateStore {
|
export class EstimateStore implements IEstimateStore {
|
||||||
// observables
|
// observables
|
||||||
estimates: Record<string, IEstimate[] | null> = {};
|
estimateMap: Record<string, IEstimate> = {};
|
||||||
|
//loaders
|
||||||
|
fetchedMap: Record<string, boolean> = {};
|
||||||
// root store
|
// root store
|
||||||
rootStore;
|
rootStore;
|
||||||
// services
|
// services
|
||||||
@ -42,18 +48,15 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
constructor(_rootStore: RootStore) {
|
constructor(_rootStore: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observables
|
// observables
|
||||||
estimates: observable,
|
estimateMap: observable,
|
||||||
|
fetchedMap: observable,
|
||||||
// computed
|
// computed
|
||||||
areEstimatesEnabledForCurrentProject: computed,
|
areEstimatesEnabledForCurrentProject: computed,
|
||||||
projectEstimates: computed,
|
projectEstimates: computed,
|
||||||
activeEstimateDetails: computed,
|
activeEstimateDetails: computed,
|
||||||
// computed actions
|
|
||||||
areEstimatesEnabledForProject: action,
|
|
||||||
getProjectEstimateById: action,
|
|
||||||
getEstimatePointValue: action,
|
|
||||||
getProjectActiveEstimateDetails: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchProjectEstimates: action,
|
fetchProjectEstimates: action,
|
||||||
|
fetchWorskpaceEstimates: action,
|
||||||
createEstimate: action,
|
createEstimate: action,
|
||||||
updateEstimate: action,
|
updateEstimate: action,
|
||||||
deleteEstimate: action,
|
deleteEstimate: action,
|
||||||
@ -79,8 +82,9 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
*/
|
*/
|
||||||
get projectEstimates() {
|
get projectEstimates() {
|
||||||
const projectId = this.rootStore.app.router.projectId;
|
const projectId = this.rootStore.app.router.projectId;
|
||||||
if (!projectId) return null;
|
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
|
||||||
return this.estimates?.[projectId] || null;
|
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return null;
|
||||||
|
return Object.values(this.estimateMap).filter((estimate) => estimate.project === projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,47 +93,49 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
get activeEstimateDetails() {
|
get activeEstimateDetails() {
|
||||||
const currentProjectDetails = this.rootStore.projectRoot.project.currentProjectDetails;
|
const currentProjectDetails = this.rootStore.projectRoot.project.currentProjectDetails;
|
||||||
if (!currentProjectDetails || !currentProjectDetails?.estimate) return null;
|
if (!currentProjectDetails || !currentProjectDetails?.estimate) return null;
|
||||||
return this.projectEstimates?.find((estimate) => estimate.id === currentProjectDetails?.estimate) || null;
|
return this.estimateMap?.[currentProjectDetails?.estimate || ""] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns true if estimates are enabled for a project using project id
|
* @description returns true if estimates are enabled for a project using project id
|
||||||
* @param projectId
|
* @param projectId
|
||||||
*/
|
*/
|
||||||
areEstimatesEnabledForProject = (projectId: string) => {
|
areEstimatesEnabledForProject = computedFn((projectId: string) => {
|
||||||
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
|
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
|
||||||
if (!projectDetails) return false;
|
if (!projectDetails) return false;
|
||||||
return Boolean(projectDetails.estimate) ?? false;
|
return Boolean(projectDetails.estimate) ?? false;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns the point value for the given estimate key to display in the UI
|
* @description returns the point value for the given estimate key to display in the UI
|
||||||
*/
|
*/
|
||||||
getEstimatePointValue = (estimateKey: number | null) => {
|
getEstimatePointValue = computedFn((estimateKey: number | null, projectId?: string) => {
|
||||||
if (estimateKey === null) return "None";
|
if (estimateKey === null) return "None";
|
||||||
const activeEstimate = this.activeEstimateDetails;
|
const activeEstimate = projectId ? this.getProjectActiveEstimateDetails(projectId) : this.activeEstimateDetails;
|
||||||
return activeEstimate?.points?.find((point) => point.key === estimateKey)?.value || "None";
|
return activeEstimate?.points?.find((point) => point.key === estimateKey)?.value || "None";
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns the estimate details for the given estimate id
|
* @description returns the estimate details for the given estimate id
|
||||||
* @param estimateId
|
* @param estimateId
|
||||||
*/
|
*/
|
||||||
getProjectEstimateById = (estimateId: string) => {
|
getProjectEstimateById = computedFn((estimateId: string) => {
|
||||||
if (!this.projectEstimates) return null;
|
if (!this.projectEstimates) return null;
|
||||||
const estimateInfo = this.projectEstimates?.find((estimate) => estimate.id === estimateId) || null;
|
const estimateInfo = this.estimateMap?.[estimateId] || null;
|
||||||
return estimateInfo;
|
return estimateInfo;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns the estimate details for the given estimate id
|
* @description returns the estimate details for the given estimate id
|
||||||
* @param projectId
|
* @param projectId
|
||||||
*/
|
*/
|
||||||
getProjectActiveEstimateDetails = (projectId: string) => {
|
getProjectActiveEstimateDetails = computedFn((projectId: string) => {
|
||||||
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
|
const projectDetails = this.rootStore.projectRoot.project?.getProjectById(projectId);
|
||||||
if (!projectDetails || !projectDetails?.estimate) return null;
|
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
|
||||||
return this.projectEstimates?.find((estimate) => estimate.id === projectDetails?.estimate) || null;
|
if (!projectDetails || !projectDetails?.estimate || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug]))
|
||||||
};
|
return null;
|
||||||
|
return this.estimateMap?.[projectDetails?.estimate || ""] || null;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetches the list of estimates for the given project
|
* @description fetches the list of estimates for the given project
|
||||||
@ -139,7 +145,26 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
fetchProjectEstimates = async (workspaceSlug: string, projectId: string) =>
|
fetchProjectEstimates = async (workspaceSlug: string, projectId: string) =>
|
||||||
await this.estimateService.getEstimatesList(workspaceSlug, projectId).then((response) => {
|
await this.estimateService.getEstimatesList(workspaceSlug, projectId).then((response) => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.estimates, projectId, response);
|
response.forEach((estimate) => {
|
||||||
|
set(this.estimateMap, estimate.id, estimate);
|
||||||
|
});
|
||||||
|
this.fetchedMap[projectId] = true;
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description fetches the list of estimates for the given project
|
||||||
|
* @param workspaceSlug
|
||||||
|
* @param projectId
|
||||||
|
*/
|
||||||
|
fetchWorskpaceEstimates = async (workspaceSlug: string) =>
|
||||||
|
await this.estimateService.getWorkspaceEstimatesList(workspaceSlug).then((response) => {
|
||||||
|
runInAction(() => {
|
||||||
|
response.forEach((estimate) => {
|
||||||
|
set(this.estimateMap, estimate.id, estimate);
|
||||||
|
});
|
||||||
|
this.fetchedMap[workspaceSlug] = true;
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
@ -157,7 +182,7 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
points: response.estimate_points,
|
points: response.estimate_points,
|
||||||
};
|
};
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.estimates, projectId, [responseEstimate, ...(this.estimates?.[projectId] || [])]);
|
set(this.estimateMap, [responseEstimate.id], responseEstimate);
|
||||||
});
|
});
|
||||||
return response.estimate;
|
return response.estimate;
|
||||||
});
|
});
|
||||||
@ -171,11 +196,12 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
*/
|
*/
|
||||||
updateEstimate = async (workspaceSlug: string, projectId: string, estimateId: string, data: IEstimateFormData) =>
|
updateEstimate = async (workspaceSlug: string, projectId: string, estimateId: string, data: IEstimateFormData) =>
|
||||||
await this.estimateService.patchEstimate(workspaceSlug, projectId, estimateId, data).then((response) => {
|
await this.estimateService.patchEstimate(workspaceSlug, projectId, estimateId, data).then((response) => {
|
||||||
const updatedEstimates = (this.estimates?.[projectId] ?? []).map((estimate) =>
|
|
||||||
estimate.id === estimateId ? { ...estimate, ...data.estimate, points: [...data.estimate_points] } : estimate
|
|
||||||
);
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.estimates, projectId, updatedEstimates);
|
set(this.estimateMap, estimateId, {
|
||||||
|
...this.estimateMap[estimateId],
|
||||||
|
...data.estimate,
|
||||||
|
points: [...data.estimate_points],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
@ -188,9 +214,8 @@ export class EstimateStore implements IEstimateStore {
|
|||||||
*/
|
*/
|
||||||
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) =>
|
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) =>
|
||||||
await this.estimateService.deleteEstimate(workspaceSlug, projectId, estimateId).then(() => {
|
await this.estimateService.deleteEstimate(workspaceSlug, projectId, estimateId).then(() => {
|
||||||
const updatedEstimates = (this.estimates?.[projectId] ?? []).filter((estimate) => estimate.id !== estimateId);
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
set(this.estimates, projectId, updatedEstimates);
|
delete this.estimateMap[estimateId];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
// services
|
// services
|
||||||
import { WorkspaceService } from "services/workspace.service";
|
import { WorkspaceService } from "services/workspace.service";
|
||||||
@ -37,9 +38,6 @@ export class GlobalViewStore implements IGlobalViewStore {
|
|||||||
globalViewMap: observable,
|
globalViewMap: observable,
|
||||||
// computed
|
// computed
|
||||||
currentWorkspaceViews: computed,
|
currentWorkspaceViews: computed,
|
||||||
// computed actions
|
|
||||||
getSearchedViews: action,
|
|
||||||
getViewDetailsById: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchAllGlobalViews: action,
|
fetchAllGlobalViews: action,
|
||||||
fetchGlobalViewDetails: action,
|
fetchGlobalViewDetails: action,
|
||||||
@ -73,7 +71,7 @@ export class GlobalViewStore implements IGlobalViewStore {
|
|||||||
* @param searchQuery
|
* @param searchQuery
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getSearchedViews = (searchQuery: string) => {
|
getSearchedViews = computedFn((searchQuery: string) => {
|
||||||
const currentWorkspaceDetails = this.rootStore.workspaceRoot.currentWorkspace;
|
const currentWorkspaceDetails = this.rootStore.workspaceRoot.currentWorkspace;
|
||||||
if (!currentWorkspaceDetails) return null;
|
if (!currentWorkspaceDetails) return null;
|
||||||
|
|
||||||
@ -84,13 +82,13 @@ export class GlobalViewStore implements IGlobalViewStore {
|
|||||||
this.globalViewMap[viewId]?.name?.toLowerCase().includes(searchQuery.toLowerCase())
|
this.globalViewMap[viewId]?.name?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
) ?? null
|
) ?? null
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns view details for given viewId
|
* @description returns view details for given viewId
|
||||||
* @param viewId
|
* @param viewId
|
||||||
*/
|
*/
|
||||||
getViewDetailsById = (viewId: string): IWorkspaceView | null => this.globalViewMap[viewId] ?? null;
|
getViewDetailsById = computedFn((viewId: string): IWorkspaceView | null => this.globalViewMap[viewId] ?? null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch all global views for given workspace
|
* @description fetch all global views for given workspace
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
// services
|
// services
|
||||||
import { InboxService } from "services/inbox.service";
|
import { InboxService } from "services/inbox.service";
|
||||||
@ -43,8 +44,6 @@ export class InboxStore implements IInboxStore {
|
|||||||
inboxDetails: observable,
|
inboxDetails: observable,
|
||||||
// computed
|
// computed
|
||||||
isInboxEnabled: computed,
|
isInboxEnabled: computed,
|
||||||
// computed actions
|
|
||||||
getInboxId: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchInboxesList: action,
|
fetchInboxesList: action,
|
||||||
});
|
});
|
||||||
@ -69,11 +68,11 @@ export class InboxStore implements IInboxStore {
|
|||||||
/**
|
/**
|
||||||
* Returns the inbox Id belongs to a specific project
|
* Returns the inbox Id belongs to a specific project
|
||||||
*/
|
*/
|
||||||
getInboxId = (projectId: string) => {
|
getInboxId = computedFn((projectId: string) => {
|
||||||
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
|
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
|
||||||
if (!projectDetails || !projectDetails.inbox_view) return null;
|
if (!projectDetails || !projectDetails.inbox_view) return null;
|
||||||
return this.inboxesList[projectId]?.[0]?.id ?? null;
|
return this.inboxesList[projectId]?.[0]?.id ?? null;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the inboxes list belongs to a specific project
|
* Fetches the inboxes list belongs to a specific project
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { observable, action, makeObservable, runInAction, autorun, computed } from "mobx";
|
import { observable, action, makeObservable, runInAction, autorun, computed } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
// services
|
// services
|
||||||
import { InboxService } from "services/inbox.service";
|
import { InboxService } from "services/inbox.service";
|
||||||
@ -61,8 +62,6 @@ export class InboxIssuesStore implements IInboxIssuesStore {
|
|||||||
issueMap: observable,
|
issueMap: observable,
|
||||||
// computed
|
// computed
|
||||||
currentInboxIssueIds: computed,
|
currentInboxIssueIds: computed,
|
||||||
// computed actions
|
|
||||||
getIssueById: action,
|
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchIssues: action,
|
fetchIssues: action,
|
||||||
fetchIssueDetails: action,
|
fetchIssueDetails: action,
|
||||||
@ -99,7 +98,9 @@ export class InboxIssuesStore implements IInboxIssuesStore {
|
|||||||
/**
|
/**
|
||||||
* Returns the issue details belongs to a specific inbox issue
|
* Returns the issue details belongs to a specific inbox issue
|
||||||
*/
|
*/
|
||||||
getIssueById = (inboxId: string, issueId: string): IInboxIssue | null => this.issueMap?.[inboxId]?.[issueId] ?? null;
|
getIssueById = computedFn(
|
||||||
|
(inboxId: string, issueId: string): IInboxIssue | null => this.issueMap?.[inboxId]?.[issueId] ?? null
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches issues of a specific inbox and adds it to the store
|
* Fetches issues of a specific inbox and adds it to the store
|
||||||
|
@ -2,6 +2,7 @@ import set from "lodash/set";
|
|||||||
import isEmpty from "lodash/isEmpty";
|
import isEmpty from "lodash/isEmpty";
|
||||||
// store
|
// store
|
||||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
// types
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TIssue } from "@plane/types";
|
||||||
|
|
||||||
@ -80,17 +81,17 @@ export class IssueStore implements IIssueStore {
|
|||||||
* @param {string} issueId
|
* @param {string} issueId
|
||||||
* @returns {TIssue | undefined}
|
* @returns {TIssue | undefined}
|
||||||
*/
|
*/
|
||||||
getIssueById = (issueId: string) => {
|
getIssueById = computedFn((issueId: string) => {
|
||||||
if (!issueId || isEmpty(this.issuesMap) || !this.issuesMap[issueId]) return undefined;
|
if (!issueId || isEmpty(this.issuesMap) || !this.issuesMap[issueId]) return undefined;
|
||||||
return this.issuesMap[issueId];
|
return this.issuesMap[issueId];
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description This method will return the issues from the issuesMap
|
* @description This method will return the issues from the issuesMap
|
||||||
* @param {string[]} issueIds
|
* @param {string[]} issueIds
|
||||||
* @returns {Record<string, TIssue> | undefined}
|
* @returns {Record<string, TIssue> | undefined}
|
||||||
*/
|
*/
|
||||||
getIssuesByIds = (issueIds: string[]) => {
|
getIssuesByIds = computedFn((issueIds: string[]) => {
|
||||||
if (!issueIds || issueIds.length <= 0 || isEmpty(this.issuesMap)) return undefined;
|
if (!issueIds || issueIds.length <= 0 || isEmpty(this.issuesMap)) return undefined;
|
||||||
const filteredIssues: { [key: string]: TIssue } = {};
|
const filteredIssues: { [key: string]: TIssue } = {};
|
||||||
Object.values(this.issuesMap).forEach((issue) => {
|
Object.values(this.issuesMap).forEach((issue) => {
|
||||||
@ -99,5 +100,5 @@ export class IssueStore implements IIssueStore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
return isEmpty(filteredIssues) ? undefined : filteredIssues;
|
return isEmpty(filteredIssues) ? undefined : filteredIssues;
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ export class IssueRootStore implements IIssueRootStore {
|
|||||||
if (rootStore.app.router.userId) this.userId = rootStore.app.router.userId;
|
if (rootStore.app.router.userId) this.userId = rootStore.app.router.userId;
|
||||||
if (!isEmpty(rootStore?.state?.stateMap)) this.states = Object.keys(rootStore?.state?.stateMap);
|
if (!isEmpty(rootStore?.state?.stateMap)) this.states = Object.keys(rootStore?.state?.stateMap);
|
||||||
if (!isEmpty(rootStore?.state?.projectStates)) this.stateDetails = rootStore?.state?.projectStates;
|
if (!isEmpty(rootStore?.state?.projectStates)) this.stateDetails = rootStore?.state?.projectStates;
|
||||||
if (!isEmpty(rootStore?.labelRoot?.labelMap)) this.labels = Object.keys(rootStore?.labelRoot?.labelMap);
|
if (!isEmpty(rootStore?.label?.labelMap)) this.labels = Object.keys(rootStore?.label?.labelMap);
|
||||||
if (!isEmpty(rootStore?.memberRoot?.workspace?.workspaceMemberMap))
|
if (!isEmpty(rootStore?.memberRoot?.workspace?.workspaceMemberMap))
|
||||||
this.members = Object.keys(rootStore?.memberRoot?.workspace?.workspaceMemberMap);
|
this.members = Object.keys(rootStore?.memberRoot?.workspace?.workspaceMemberMap);
|
||||||
if (!isEmpty(rootStore?.projectRoot?.project?.projectMap))
|
if (!isEmpty(rootStore?.projectRoot?.project?.projectMap))
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
|
import sortBy from "lodash/sortBy";
|
||||||
// services
|
// services
|
||||||
import { IssueLabelService } from "services/issue";
|
import { IssueLabelService } from "services/issue";
|
||||||
// helpers
|
// helpers
|
||||||
@ -7,17 +9,21 @@ import { buildTree } from "helpers/array.helper";
|
|||||||
// types
|
// types
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
import { IIssueLabel, IIssueLabelTree } from "@plane/types";
|
import { IIssueLabel, IIssueLabelTree } from "@plane/types";
|
||||||
import { ILabelRootStore } from "store/label";
|
|
||||||
|
|
||||||
export interface IProjectLabelStore {
|
export interface ILabelStore {
|
||||||
//Loaders
|
//Loaders
|
||||||
fetchedMap: Record<string, boolean>;
|
fetchedMap: Record<string, boolean>;
|
||||||
|
//Observable
|
||||||
|
labelMap: Record<string, IIssueLabel>;
|
||||||
// computed
|
// computed
|
||||||
projectLabels: IIssueLabel[] | undefined;
|
projectLabels: IIssueLabel[] | undefined;
|
||||||
projectLabelsTree: IIssueLabelTree[] | undefined;
|
projectLabelsTree: IIssueLabelTree[] | undefined;
|
||||||
|
workspaceLabels: IIssueLabel[] | undefined;
|
||||||
//computed actions
|
//computed actions
|
||||||
getProjectLabels: (projectId: string) => IIssueLabel[] | undefined;
|
getProjectLabels: (projectId: string | null) => IIssueLabel[] | undefined;
|
||||||
|
getLabelById: (labelId: string) => IIssueLabel | null;
|
||||||
// fetch actions
|
// fetch actions
|
||||||
|
fetchWorkspaceLabels: (workspaceSlug: string) => Promise<IIssueLabel[]>;
|
||||||
fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise<IIssueLabel[]>;
|
fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise<IIssueLabel[]>;
|
||||||
// crud actions
|
// crud actions
|
||||||
createLabel: (workspaceSlug: string, projectId: string, data: Partial<IIssueLabel>) => Promise<IIssueLabel>;
|
createLabel: (workspaceSlug: string, projectId: string, data: Partial<IIssueLabel>) => Promise<IIssueLabel>;
|
||||||
@ -39,7 +45,7 @@ export interface IProjectLabelStore {
|
|||||||
deleteLabel: (workspaceSlug: string, projectId: string, labelId: string) => Promise<void>;
|
deleteLabel: (workspaceSlug: string, projectId: string, labelId: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProjectLabelStore implements IProjectLabelStore {
|
export class LabelStore implements ILabelStore {
|
||||||
// root store
|
// root store
|
||||||
rootStore;
|
rootStore;
|
||||||
// root store labelMap
|
// root store labelMap
|
||||||
@ -49,15 +55,13 @@ export class ProjectLabelStore implements IProjectLabelStore {
|
|||||||
// services
|
// services
|
||||||
issueLabelService;
|
issueLabelService;
|
||||||
|
|
||||||
constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) {
|
constructor(_rootStore: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
labelMap: observable,
|
labelMap: observable,
|
||||||
fetchedMap: observable,
|
fetchedMap: observable,
|
||||||
// computed
|
// computed
|
||||||
projectLabels: computed,
|
projectLabels: computed,
|
||||||
projectLabelsTree: computed,
|
projectLabelsTree: computed,
|
||||||
// actions
|
|
||||||
getProjectLabels: action,
|
|
||||||
|
|
||||||
fetchProjectLabels: action,
|
fetchProjectLabels: action,
|
||||||
createLabel: action,
|
createLabel: action,
|
||||||
@ -68,18 +72,34 @@ export class ProjectLabelStore implements IProjectLabelStore {
|
|||||||
|
|
||||||
// root store
|
// root store
|
||||||
this.rootStore = _rootStore;
|
this.rootStore = _rootStore;
|
||||||
this.labelMap = _labelRoot?.labelMap;
|
|
||||||
// services
|
// services
|
||||||
this.issueLabelService = new IssueLabelService();
|
this.issueLabelService = new IssueLabelService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the labelMap belongs to a specific workspace
|
||||||
|
*/
|
||||||
|
get workspaceLabels() {
|
||||||
|
const currentWorkspaceDetails = this.rootStore.workspaceRoot.currentWorkspace;
|
||||||
|
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
|
||||||
|
if (!currentWorkspaceDetails || !this.fetchedMap[worksapceSlug]) return;
|
||||||
|
return sortBy(
|
||||||
|
Object.values(this.labelMap).filter((label) => label.workspace_id === currentWorkspaceDetails.id),
|
||||||
|
"sort_order"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the labelMap belonging to the current project
|
* Returns the labelMap belonging to the current project
|
||||||
*/
|
*/
|
||||||
get projectLabels() {
|
get projectLabels() {
|
||||||
const projectId = this.rootStore.app.router.projectId;
|
const projectId = this.rootStore.app.router.projectId;
|
||||||
if (!projectId || !this.fetchedMap[projectId] || !this.labelMap) return;
|
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
|
||||||
return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId);
|
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return;
|
||||||
|
return sortBy(
|
||||||
|
Object.values(this.labelMap).filter((label) => label.project_id === projectId),
|
||||||
|
"sort_order"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -90,10 +110,20 @@ export class ProjectLabelStore implements IProjectLabelStore {
|
|||||||
return buildTree(this.projectLabels);
|
return buildTree(this.projectLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjectLabels = (projectId: string) => {
|
getProjectLabels = computedFn((projectId: string | null) => {
|
||||||
if (!this.fetchedMap[projectId] || !this.labelMap) return;
|
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
|
||||||
return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId);
|
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return;
|
||||||
};
|
return sortBy(
|
||||||
|
Object.values(this.labelMap).filter((label) => label.project_id === projectId),
|
||||||
|
"sort_order"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get label info from the map of labels in the store using label id
|
||||||
|
* @param labelId
|
||||||
|
*/
|
||||||
|
getLabelById = computedFn((labelId: string): IIssueLabel | null => this.labelMap?.[labelId] || null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all the labelMap belongs to a specific project
|
* Fetches all the labelMap belongs to a specific project
|
||||||
@ -112,6 +142,23 @@ export class ProjectLabelStore implements IProjectLabelStore {
|
|||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all the labelMap belongs to a specific project
|
||||||
|
* @param workspaceSlug
|
||||||
|
* @param projectId
|
||||||
|
* @returns Promise<IIssueLabel[]>
|
||||||
|
*/
|
||||||
|
fetchWorkspaceLabels = async (workspaceSlug: string) =>
|
||||||
|
await this.issueLabelService.getWorkspaceIssueLabels(workspaceSlug).then((response) => {
|
||||||
|
runInAction(() => {
|
||||||
|
response.forEach((label) => {
|
||||||
|
set(this.labelMap, [label.id], label);
|
||||||
|
});
|
||||||
|
set(this.fetchedMap, workspaceSlug, true);
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new label for a specific project and add it to the store
|
* Creates a new label for a specific project and add it to the store
|
||||||
* @param workspaceSlug
|
* @param workspaceSlug
|
@ -1,42 +0,0 @@
|
|||||||
import { observable, makeObservable, action } from "mobx";
|
|
||||||
import { RootStore } from "../root.store";
|
|
||||||
// types
|
|
||||||
import { IIssueLabel } from "@plane/types";
|
|
||||||
import { IProjectLabelStore, ProjectLabelStore } from "./project-label.store";
|
|
||||||
import { IWorkspaceLabelStore, WorkspaceLabelStore } from "./workspace-label.store";
|
|
||||||
|
|
||||||
export interface ILabelRootStore {
|
|
||||||
// observables
|
|
||||||
labelMap: Record<string, IIssueLabel>;
|
|
||||||
// computed actions
|
|
||||||
getLabelById: (labelId: string) => IIssueLabel | null;
|
|
||||||
// sub-stores
|
|
||||||
project: IProjectLabelStore;
|
|
||||||
workspace: IWorkspaceLabelStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class LabelRootStore implements ILabelRootStore {
|
|
||||||
// observables
|
|
||||||
labelMap: Record<string, IIssueLabel> = {};
|
|
||||||
// sub-stores
|
|
||||||
project: IProjectLabelStore;
|
|
||||||
workspace: IWorkspaceLabelStore;
|
|
||||||
|
|
||||||
constructor(_rootStore: RootStore) {
|
|
||||||
makeObservable(this, {
|
|
||||||
// observables
|
|
||||||
labelMap: observable,
|
|
||||||
// computed actions
|
|
||||||
getLabelById: action,
|
|
||||||
});
|
|
||||||
// sub-stores
|
|
||||||
this.project = new ProjectLabelStore(this, _rootStore);
|
|
||||||
this.workspace = new WorkspaceLabelStore(this, _rootStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get label info from the map of labels in the store using label id
|
|
||||||
* @param labelId
|
|
||||||
*/
|
|
||||||
getLabelById = (labelId: string): IIssueLabel | null => this.labelMap?.[labelId] || null;
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
import { action, computed, makeObservable, runInAction } from "mobx";
|
|
||||||
import { set } from "lodash";
|
|
||||||
// services
|
|
||||||
import { IssueLabelService } from "services/issue";
|
|
||||||
// types
|
|
||||||
import { RootStore } from "store/root.store";
|
|
||||||
import { IIssueLabel } from "@plane/types";
|
|
||||||
import { ILabelRootStore } from "store/label";
|
|
||||||
|
|
||||||
export interface IWorkspaceLabelStore {
|
|
||||||
// computed
|
|
||||||
workspaceLabels: IIssueLabel[] | undefined;
|
|
||||||
// fetch actions
|
|
||||||
fetchWorkspaceLabels: (workspaceSlug: string) => Promise<IIssueLabel[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkspaceLabelStore implements IWorkspaceLabelStore {
|
|
||||||
// root store
|
|
||||||
rootStore;
|
|
||||||
// root store labelMap
|
|
||||||
labelMap: Record<string, IIssueLabel> = {};
|
|
||||||
// services
|
|
||||||
issueLabelService;
|
|
||||||
|
|
||||||
constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) {
|
|
||||||
makeObservable(this, {
|
|
||||||
// computed
|
|
||||||
workspaceLabels: computed,
|
|
||||||
// actions
|
|
||||||
fetchWorkspaceLabels: action,
|
|
||||||
});
|
|
||||||
|
|
||||||
// root store
|
|
||||||
this.rootStore = _rootStore;
|
|
||||||
this.labelMap = _labelRoot?.labelMap;
|
|
||||||
// services
|
|
||||||
this.issueLabelService = new IssueLabelService();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the labelMap belongs to a specific workspace
|
|
||||||
*/
|
|
||||||
get workspaceLabels() {
|
|
||||||
const currentWorkspaceDetails = this.rootStore.workspaceRoot.currentWorkspace;
|
|
||||||
if (!currentWorkspaceDetails) return;
|
|
||||||
return Object.values(this.labelMap).filter((label) => label.workspace === currentWorkspaceDetails.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches all the labelMap belongs to a specific project
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @returns Promise<IIssueLabel[]>
|
|
||||||
*/
|
|
||||||
fetchWorkspaceLabels = async (workspaceSlug: string) =>
|
|
||||||
await this.issueLabelService.getWorkspaceIssueLabels(workspaceSlug).then((response) => {
|
|
||||||
runInAction(() => {
|
|
||||||
response.forEach((label) => {
|
|
||||||
set(this.labelMap, [label.id], label);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
// services
|
// services
|
||||||
@ -64,9 +65,6 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
|||||||
projectMemberMap: observable,
|
projectMemberMap: observable,
|
||||||
// computed
|
// computed
|
||||||
projectMemberIds: computed,
|
projectMemberIds: computed,
|
||||||
// computed actions
|
|
||||||
getProjectMemberDetails: action,
|
|
||||||
getProjectMemberIds: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchProjectMembers: action,
|
fetchProjectMembers: action,
|
||||||
bulkAddMembersToProject: action,
|
bulkAddMembersToProject: action,
|
||||||
@ -101,7 +99,7 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
|||||||
* @description get the details of a project member
|
* @description get the details of a project member
|
||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
getProjectMemberDetails = (userId: string) => {
|
getProjectMemberDetails = computedFn((userId: string) => {
|
||||||
const projectId = this.routerStore.projectId;
|
const projectId = this.routerStore.projectId;
|
||||||
if (!projectId) return null;
|
if (!projectId) return null;
|
||||||
const projectMember = this.projectMemberMap?.[projectId]?.[userId];
|
const projectMember = this.projectMemberMap?.[projectId]?.[userId];
|
||||||
@ -113,13 +111,13 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
|||||||
member: this.memberRoot?.memberMap?.[projectMember.member],
|
member: this.memberRoot?.memberMap?.[projectMember.member],
|
||||||
};
|
};
|
||||||
return memberDetails;
|
return memberDetails;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get the list of all the user ids of all the members of a project using projectId
|
* @description get the list of all the user ids of all the members of a project using projectId
|
||||||
* @param projectId
|
* @param projectId
|
||||||
*/
|
*/
|
||||||
getProjectMemberIds = (projectId: string): string[] | null => {
|
getProjectMemberIds = computedFn((projectId: string): string[] | null => {
|
||||||
if (!this.projectMemberMap?.[projectId]) return null;
|
if (!this.projectMemberMap?.[projectId]) return null;
|
||||||
let members = Object.values(this.projectMemberMap?.[projectId]);
|
let members = Object.values(this.projectMemberMap?.[projectId]);
|
||||||
members = sortBy(members, [
|
members = sortBy(members, [
|
||||||
@ -128,7 +126,7 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
|||||||
]);
|
]);
|
||||||
const memberIds = members.map((m) => m.member);
|
const memberIds = members.map((m) => m.member);
|
||||||
return memberIds;
|
return memberIds;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch the list of all the members of a project
|
* @description fetch the list of all the members of a project
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
// services
|
// services
|
||||||
@ -67,11 +68,6 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
// computed
|
// computed
|
||||||
workspaceMemberIds: computed,
|
workspaceMemberIds: computed,
|
||||||
workspaceMemberInvitationIds: computed,
|
workspaceMemberInvitationIds: computed,
|
||||||
// computed actions
|
|
||||||
getSearchedWorkspaceMemberIds: action,
|
|
||||||
getSearchedWorkspaceInvitationIds: action,
|
|
||||||
getWorkspaceMemberDetails: action,
|
|
||||||
getWorkspaceInvitationDetails: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchWorkspaceMembers: action,
|
fetchWorkspaceMembers: action,
|
||||||
updateMember: action,
|
updateMember: action,
|
||||||
@ -114,7 +110,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
* @description get the list of all the user ids that match the search query of all the members of the current workspace
|
* @description get the list of all the user ids that match the search query of all the members of the current workspace
|
||||||
* @param searchQuery
|
* @param searchQuery
|
||||||
*/
|
*/
|
||||||
getSearchedWorkspaceMemberIds = (searchQuery: string) => {
|
getSearchedWorkspaceMemberIds = computedFn((searchQuery: string) => {
|
||||||
const workspaceSlug = this.routerStore.workspaceSlug;
|
const workspaceSlug = this.routerStore.workspaceSlug;
|
||||||
if (!workspaceSlug) return null;
|
if (!workspaceSlug) return null;
|
||||||
const workspaceMemberIds = this.workspaceMemberIds;
|
const workspaceMemberIds = this.workspaceMemberIds;
|
||||||
@ -128,13 +124,13 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
return memberSearchQuery.toLowerCase()?.includes(searchQuery.toLowerCase());
|
return memberSearchQuery.toLowerCase()?.includes(searchQuery.toLowerCase());
|
||||||
});
|
});
|
||||||
return searchedWorkspaceMemberIds;
|
return searchedWorkspaceMemberIds;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get the list of all the invitation ids that match the search query of all the member invitations of the current workspace
|
* @description get the list of all the invitation ids that match the search query of all the member invitations of the current workspace
|
||||||
* @param searchQuery
|
* @param searchQuery
|
||||||
*/
|
*/
|
||||||
getSearchedWorkspaceInvitationIds = (searchQuery: string) => {
|
getSearchedWorkspaceInvitationIds = computedFn((searchQuery: string) => {
|
||||||
const workspaceSlug = this.routerStore.workspaceSlug;
|
const workspaceSlug = this.routerStore.workspaceSlug;
|
||||||
if (!workspaceSlug) return null;
|
if (!workspaceSlug) return null;
|
||||||
const workspaceMemberInvitationIds = this.workspaceMemberInvitationIds;
|
const workspaceMemberInvitationIds = this.workspaceMemberInvitationIds;
|
||||||
@ -146,13 +142,13 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
return invitationSearchQuery.toLowerCase()?.includes(searchQuery.toLowerCase());
|
return invitationSearchQuery.toLowerCase()?.includes(searchQuery.toLowerCase());
|
||||||
});
|
});
|
||||||
return searchedWorkspaceMemberInvitationIds;
|
return searchedWorkspaceMemberInvitationIds;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get the details of a workspace member
|
* @description get the details of a workspace member
|
||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
getWorkspaceMemberDetails = (userId: string) => {
|
getWorkspaceMemberDetails = computedFn((userId: string) => {
|
||||||
const workspaceSlug = this.routerStore.workspaceSlug;
|
const workspaceSlug = this.routerStore.workspaceSlug;
|
||||||
if (!workspaceSlug) return null;
|
if (!workspaceSlug) return null;
|
||||||
const workspaceMember = this.workspaceMemberMap?.[workspaceSlug]?.[userId];
|
const workspaceMember = this.workspaceMemberMap?.[workspaceSlug]?.[userId];
|
||||||
@ -164,14 +160,14 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
member: this.memberRoot?.memberMap?.[workspaceMember.member],
|
member: this.memberRoot?.memberMap?.[workspaceMember.member],
|
||||||
};
|
};
|
||||||
return memberDetails;
|
return memberDetails;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get the details of a workspace member invitation
|
* @description get the details of a workspace member invitation
|
||||||
* @param workspaceSlug
|
* @param workspaceSlug
|
||||||
* @param memberId
|
* @param memberId
|
||||||
*/
|
*/
|
||||||
getWorkspaceInvitationDetails = (invitationId: string) => {
|
getWorkspaceInvitationDetails = computedFn((invitationId: string) => {
|
||||||
const workspaceSlug = this.routerStore.workspaceSlug;
|
const workspaceSlug = this.routerStore.workspaceSlug;
|
||||||
if (!workspaceSlug) return null;
|
if (!workspaceSlug) return null;
|
||||||
const invitationsList = this.workspaceMemberInvitations?.[workspaceSlug];
|
const invitationsList = this.workspaceMemberInvitations?.[workspaceSlug];
|
||||||
@ -179,7 +175,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||||||
|
|
||||||
const invitation = invitationsList.find((inv) => inv.id === invitationId);
|
const invitation = invitationsList.find((inv) => inv.id === invitationId);
|
||||||
return invitation ?? null;
|
return invitation ?? null;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch all the members of a workspace
|
* @description fetch all the members of a workspace
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
import { action, computed, observable, makeObservable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import sortBy from "lodash/sortBy";
|
import sortBy from "lodash/sortBy";
|
||||||
// services
|
// services
|
||||||
@ -68,9 +69,6 @@ export class ModulesStore implements IModuleStore {
|
|||||||
fetchedMap: observable,
|
fetchedMap: observable,
|
||||||
// computed
|
// computed
|
||||||
projectModuleIds: computed,
|
projectModuleIds: computed,
|
||||||
// computed actions
|
|
||||||
getModuleById: action,
|
|
||||||
getProjectModuleIds: action,
|
|
||||||
// actions
|
// actions
|
||||||
fetchModules: action,
|
fetchModules: action,
|
||||||
fetchModuleDetails: action,
|
fetchModuleDetails: action,
|
||||||
@ -109,20 +107,20 @@ export class ModulesStore implements IModuleStore {
|
|||||||
* @param moduleId
|
* @param moduleId
|
||||||
* @returns IModule | null
|
* @returns IModule | null
|
||||||
*/
|
*/
|
||||||
getModuleById = (moduleId: string) => this.moduleMap?.[moduleId] || null;
|
getModuleById = computedFn((moduleId: string) => this.moduleMap?.[moduleId] || null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description returns list of module ids of the project id passed as argument
|
* @description returns list of module ids of the project id passed as argument
|
||||||
* @param projectId
|
* @param projectId
|
||||||
*/
|
*/
|
||||||
getProjectModuleIds = (projectId: string) => {
|
getProjectModuleIds = computedFn((projectId: string) => {
|
||||||
if (!this.fetchedMap[projectId]) return null;
|
if (!this.fetchedMap[projectId]) return null;
|
||||||
|
|
||||||
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
|
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
|
||||||
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]);
|
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]);
|
||||||
const projectModuleIds = projectModules.map((m) => m.id);
|
const projectModuleIds = projectModules.map((m) => m.id);
|
||||||
return projectModuleIds;
|
return projectModuleIds;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch all modules
|
* @description fetch all modules
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { set } from "lodash";
|
import { set } from "lodash";
|
||||||
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
import { observable, action, makeObservable, runInAction, computed } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
// services
|
// services
|
||||||
import { ViewService } from "services/view.service";
|
import { ViewService } from "services/view.service";
|
||||||
import { RootStore } from "store/root.store";
|
import { RootStore } from "store/root.store";
|
||||||
@ -49,8 +50,6 @@ export class ProjectViewStore implements IProjectViewStore {
|
|||||||
fetchedMap: observable,
|
fetchedMap: observable,
|
||||||
// computed
|
// computed
|
||||||
projectViewIds: computed,
|
projectViewIds: computed,
|
||||||
// computed actions
|
|
||||||
getViewById: action,
|
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchViews: action,
|
fetchViews: action,
|
||||||
fetchViewDetails: action,
|
fetchViewDetails: action,
|
||||||
@ -81,7 +80,7 @@ export class ProjectViewStore implements IProjectViewStore {
|
|||||||
/**
|
/**
|
||||||
* Returns view details by id
|
* Returns view details by id
|
||||||
*/
|
*/
|
||||||
getViewById = (viewId: string) => this.viewMap?.[viewId] ?? null;
|
getViewById = computedFn((viewId: string) => this.viewMap?.[viewId] ?? null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches views for current project
|
* Fetches views for current project
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
// types
|
// types
|
||||||
import { RootStore } from "../root.store";
|
import { RootStore } from "../root.store";
|
||||||
@ -6,7 +7,6 @@ import { IProject } from "@plane/types";
|
|||||||
// services
|
// services
|
||||||
import { IssueLabelService, IssueService } from "services/issue";
|
import { IssueLabelService, IssueService } from "services/issue";
|
||||||
import { ProjectService, ProjectStateService } from "services/project";
|
import { ProjectService, ProjectStateService } from "services/project";
|
||||||
|
|
||||||
export interface IProjectStore {
|
export interface IProjectStore {
|
||||||
// observables
|
// observables
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@ -64,7 +64,7 @@ export class ProjectStore implements IProjectStore {
|
|||||||
joinedProjectIds: computed,
|
joinedProjectIds: computed,
|
||||||
favoriteProjectIds: computed,
|
favoriteProjectIds: computed,
|
||||||
// actions
|
// actions
|
||||||
setSearchQuery: action,
|
setSearchQuery: action.bound,
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchProjects: action,
|
fetchProjects: action,
|
||||||
fetchProjectDetails: action,
|
fetchProjectDetails: action,
|
||||||
@ -199,10 +199,10 @@ export class ProjectStore implements IProjectStore {
|
|||||||
* @param projectId
|
* @param projectId
|
||||||
* @returns IProject | null
|
* @returns IProject | null
|
||||||
*/
|
*/
|
||||||
getProjectById = (projectId: string) => {
|
getProjectById = computedFn((projectId: string) => {
|
||||||
const projectInfo = this.projectMap[projectId] || null;
|
const projectInfo = this.projectMap[projectId] || null;
|
||||||
return projectInfo;
|
return projectInfo;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds project to favorites and updates project favorite status in the store
|
* Adds project to favorites and updates project favorite status in the store
|
||||||
|
@ -9,7 +9,6 @@ import { IUserRootStore, UserRootStore } from "./user";
|
|||||||
import { IWorkspaceRootStore, WorkspaceRootStore } from "./workspace";
|
import { IWorkspaceRootStore, WorkspaceRootStore } from "./workspace";
|
||||||
import { IssueRootStore, IIssueRootStore } from "./issue/root.store";
|
import { IssueRootStore, IIssueRootStore } from "./issue/root.store";
|
||||||
import { IStateStore, StateStore } from "./state.store";
|
import { IStateStore, StateStore } from "./state.store";
|
||||||
import { ILabelRootStore, LabelRootStore } from "./label";
|
|
||||||
import { IMemberRootStore, MemberRootStore } from "./member";
|
import { IMemberRootStore, MemberRootStore } from "./member";
|
||||||
import { IInboxRootStore, InboxRootStore } from "./inbox";
|
import { IInboxRootStore, InboxRootStore } from "./inbox";
|
||||||
import { IEstimateStore, EstimateStore } from "./estimate.store";
|
import { IEstimateStore, EstimateStore } from "./estimate.store";
|
||||||
@ -17,6 +16,7 @@ import { GlobalViewStore, IGlobalViewStore } from "./global-view.store";
|
|||||||
import { IMentionStore, MentionStore } from "./mention.store";
|
import { IMentionStore, MentionStore } from "./mention.store";
|
||||||
import { DashboardStore, IDashboardStore } from "./dashboard.store";
|
import { DashboardStore, IDashboardStore } from "./dashboard.store";
|
||||||
import { IProjectPageStore, ProjectPageStore } from "./project-page.store";
|
import { IProjectPageStore, ProjectPageStore } from "./project-page.store";
|
||||||
|
import { ILabelStore, LabelStore } from "./label.store";
|
||||||
|
|
||||||
enableStaticRendering(typeof window === "undefined");
|
enableStaticRendering(typeof window === "undefined");
|
||||||
|
|
||||||
@ -25,7 +25,6 @@ export class RootStore {
|
|||||||
user: IUserRootStore;
|
user: IUserRootStore;
|
||||||
workspaceRoot: IWorkspaceRootStore;
|
workspaceRoot: IWorkspaceRootStore;
|
||||||
projectRoot: IProjectRootStore;
|
projectRoot: IProjectRootStore;
|
||||||
labelRoot: ILabelRootStore;
|
|
||||||
memberRoot: IMemberRootStore;
|
memberRoot: IMemberRootStore;
|
||||||
inboxRoot: IInboxRootStore;
|
inboxRoot: IInboxRootStore;
|
||||||
cycle: ICycleStore;
|
cycle: ICycleStore;
|
||||||
@ -34,6 +33,7 @@ export class RootStore {
|
|||||||
globalView: IGlobalViewStore;
|
globalView: IGlobalViewStore;
|
||||||
issue: IIssueRootStore;
|
issue: IIssueRootStore;
|
||||||
state: IStateStore;
|
state: IStateStore;
|
||||||
|
label: ILabelStore;
|
||||||
estimate: IEstimateStore;
|
estimate: IEstimateStore;
|
||||||
mention: IMentionStore;
|
mention: IMentionStore;
|
||||||
dashboard: IDashboardStore;
|
dashboard: IDashboardStore;
|
||||||
@ -44,7 +44,6 @@ export class RootStore {
|
|||||||
this.user = new UserRootStore(this);
|
this.user = new UserRootStore(this);
|
||||||
this.workspaceRoot = new WorkspaceRootStore(this);
|
this.workspaceRoot = new WorkspaceRootStore(this);
|
||||||
this.projectRoot = new ProjectRootStore(this);
|
this.projectRoot = new ProjectRootStore(this);
|
||||||
this.labelRoot = new LabelRootStore(this);
|
|
||||||
this.memberRoot = new MemberRootStore(this);
|
this.memberRoot = new MemberRootStore(this);
|
||||||
this.inboxRoot = new InboxRootStore(this);
|
this.inboxRoot = new InboxRootStore(this);
|
||||||
// independent stores
|
// independent stores
|
||||||
@ -54,6 +53,7 @@ export class RootStore {
|
|||||||
this.globalView = new GlobalViewStore(this);
|
this.globalView = new GlobalViewStore(this);
|
||||||
this.issue = new IssueRootStore(this);
|
this.issue = new IssueRootStore(this);
|
||||||
this.state = new StateStore(this);
|
this.state = new StateStore(this);
|
||||||
|
this.label = new LabelStore(this);
|
||||||
this.estimate = new EstimateStore(this);
|
this.estimate = new EstimateStore(this);
|
||||||
this.mention = new MentionStore(this);
|
this.mention = new MentionStore(this);
|
||||||
this.projectPages = new ProjectPageStore(this);
|
this.projectPages = new ProjectPageStore(this);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { makeObservable, observable, computed, action, runInAction } from "mobx";
|
import { makeObservable, observable, computed, action, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils"
|
||||||
import groupBy from "lodash/groupBy";
|
import groupBy from "lodash/groupBy";
|
||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
// store
|
// store
|
||||||
@ -21,6 +22,7 @@ export interface IStateStore {
|
|||||||
getProjectStates: (projectId: string) => IState[] | undefined;
|
getProjectStates: (projectId: string) => IState[] | undefined;
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise<IState[]>;
|
fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise<IState[]>;
|
||||||
|
fetchWorkspaceStates: (workspaceSlug: string) => Promise<IState[]>;
|
||||||
// crud actions
|
// crud actions
|
||||||
createState: (workspaceSlug: string, projectId: string, data: Partial<IState>) => Promise<IState>;
|
createState: (workspaceSlug: string, projectId: string, data: Partial<IState>) => Promise<IState>;
|
||||||
updateState: (
|
updateState: (
|
||||||
@ -55,9 +57,6 @@ export class StateStore implements IStateStore {
|
|||||||
// computed
|
// computed
|
||||||
projectStates: computed,
|
projectStates: computed,
|
||||||
groupedProjectStates: computed,
|
groupedProjectStates: computed,
|
||||||
// computed actions
|
|
||||||
getStateById: action,
|
|
||||||
getProjectStates: action,
|
|
||||||
// fetch action
|
// fetch action
|
||||||
fetchProjectStates: action,
|
fetchProjectStates: action,
|
||||||
// CRUD actions
|
// CRUD actions
|
||||||
@ -76,9 +75,10 @@ export class StateStore implements IStateStore {
|
|||||||
* Returns the stateMap belongs to a specific project
|
* Returns the stateMap belongs to a specific project
|
||||||
*/
|
*/
|
||||||
get projectStates() {
|
get projectStates() {
|
||||||
const projectId = this.router.query?.projectId?.toString();
|
const projectId = this.router.projectId;
|
||||||
if (!projectId || !this.fetchedMap[projectId]) return;
|
const worksapceSlug = this.router.workspaceSlug || "";
|
||||||
return Object.values(this.stateMap).filter((state) => state.project === this.router.query.projectId);
|
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return;
|
||||||
|
return Object.values(this.stateMap).filter((state) => state.project_id === projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,20 +93,21 @@ export class StateStore implements IStateStore {
|
|||||||
* @description returns state details using state id
|
* @description returns state details using state id
|
||||||
* @param stateId
|
* @param stateId
|
||||||
*/
|
*/
|
||||||
getStateById = (stateId: string) => {
|
getStateById = computedFn((stateId: string) => {
|
||||||
if (!this.stateMap) return;
|
if (!this.stateMap) return;
|
||||||
return this.stateMap[stateId] ?? undefined;
|
return this.stateMap[stateId] ?? undefined;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the stateMap belongs to a project by projectId
|
* Returns the stateMap belongs to a project by projectId
|
||||||
* @param projectId
|
* @param projectId
|
||||||
* @returns IState[]
|
* @returns IState[]
|
||||||
*/
|
*/
|
||||||
getProjectStates = (projectId: string) => {
|
getProjectStates = computedFn((projectId: string) => {
|
||||||
if (!projectId || !this.fetchedMap[projectId]) return;
|
const worksapceSlug = this.router.workspaceSlug || "";
|
||||||
return Object.values(this.stateMap).filter((state) => state.project === projectId);
|
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return;
|
||||||
};
|
return Object.values(this.stateMap).filter((state) => state.project_id === projectId);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetches the stateMap of a project
|
* fetches the stateMap of a project
|
||||||
@ -125,6 +126,22 @@ export class StateStore implements IStateStore {
|
|||||||
return statesResponse;
|
return statesResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fetches the stateMap of all the states in workspace
|
||||||
|
* @param workspaceSlug
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
fetchWorkspaceStates = async (workspaceSlug: string) => {
|
||||||
|
const statesResponse = await this.stateService.getWorkspaceStates(workspaceSlug);
|
||||||
|
runInAction(() => {
|
||||||
|
statesResponse.forEach((state) => {
|
||||||
|
set(this.stateMap, [state.id], state);
|
||||||
|
});
|
||||||
|
set(this.fetchedMap, workspaceSlug, true);
|
||||||
|
});
|
||||||
|
return statesResponse;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a new state in a project and adds it to the store
|
* creates a new state in a project and adds it to the store
|
||||||
* @param workspaceSlug
|
* @param workspaceSlug
|
||||||
@ -191,7 +208,7 @@ export class StateStore implements IStateStore {
|
|||||||
markStateAsDefault = async (workspaceSlug: string, projectId: string, stateId: string) => {
|
markStateAsDefault = async (workspaceSlug: string, projectId: string, stateId: string) => {
|
||||||
const originalStates = this.stateMap;
|
const originalStates = this.stateMap;
|
||||||
const currentDefaultState = Object.values(this.stateMap).find(
|
const currentDefaultState = Object.values(this.stateMap).find(
|
||||||
(state) => state.project === projectId && state.default
|
(state) => state.project_id === projectId && state.default
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// mobx
|
// mobx
|
||||||
import { action, observable, makeObservable, runInAction } from "mobx";
|
import { action, observable, makeObservable, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { APITokenService } from "services/api_token.service";
|
import { APITokenService } from "services/api_token.service";
|
||||||
import { RootStore } from "../root.store";
|
import { RootStore } from "../root.store";
|
||||||
// types
|
// types
|
||||||
@ -30,8 +31,6 @@ export class ApiTokenStore implements IApiTokenStore {
|
|||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
// observables
|
// observables
|
||||||
apiTokens: observable,
|
apiTokens: observable,
|
||||||
// computed actions
|
|
||||||
getApiTokenById: action,
|
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchApiTokens: action,
|
fetchApiTokens: action,
|
||||||
fetchApiTokenDetails: action,
|
fetchApiTokenDetails: action,
|
||||||
@ -49,10 +48,10 @@ export class ApiTokenStore implements IApiTokenStore {
|
|||||||
* get API token by id
|
* get API token by id
|
||||||
* @param apiTokenId
|
* @param apiTokenId
|
||||||
*/
|
*/
|
||||||
getApiTokenById = (apiTokenId: string) => {
|
getApiTokenById = computedFn((apiTokenId: string) => {
|
||||||
if (!this.apiTokens) return null;
|
if (!this.apiTokens) return null;
|
||||||
return this.apiTokens[apiTokenId] || null;
|
return this.apiTokens[apiTokenId] || null;
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetch all the API tokens for a workspace
|
* fetch all the API tokens for a workspace
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// mobx
|
// mobx
|
||||||
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
import { action, observable, makeObservable, computed, runInAction } from "mobx";
|
||||||
|
import { computedFn } from "mobx-utils";
|
||||||
import { IWebhook } from "@plane/types";
|
import { IWebhook } from "@plane/types";
|
||||||
import { WebhookService } from "services/webhook.service";
|
import { WebhookService } from "services/webhook.service";
|
||||||
import { RootStore } from "../root.store";
|
import { RootStore } from "../root.store";
|
||||||
@ -46,8 +47,6 @@ export class WebhookStore implements IWebhookStore {
|
|||||||
webhookSecretKey: observable.ref,
|
webhookSecretKey: observable.ref,
|
||||||
// computed
|
// computed
|
||||||
currentWebhook: computed,
|
currentWebhook: computed,
|
||||||
// computed actions
|
|
||||||
getWebhookById: action,
|
|
||||||
// fetch actions
|
// fetch actions
|
||||||
fetchWebhooks: action,
|
fetchWebhooks: action,
|
||||||
fetchWebhookById: action,
|
fetchWebhookById: action,
|
||||||
@ -80,7 +79,7 @@ export class WebhookStore implements IWebhookStore {
|
|||||||
* get webhook info from the object of webhooks in the store using webhook id
|
* get webhook info from the object of webhooks in the store using webhook id
|
||||||
* @param webhookId
|
* @param webhookId
|
||||||
*/
|
*/
|
||||||
getWebhookById = (webhookId: string) => this.webhooks?.[webhookId] || null;
|
getWebhookById = computedFn((webhookId: string) => this.webhooks?.[webhookId] || null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetch all the webhooks for a workspace
|
* fetch all the webhooks for a workspace
|
||||||
|
27
yarn.lock
27
yarn.lock
@ -2406,6 +2406,13 @@
|
|||||||
lodash.merge "^4.6.2"
|
lodash.merge "^4.6.2"
|
||||||
postcss-selector-parser "6.0.10"
|
postcss-selector-parser "6.0.10"
|
||||||
|
|
||||||
|
"@tippyjs/react@^4.2.6":
|
||||||
|
version "4.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.6.tgz#971677a599bf663f20bb1c60a62b9555b749cc71"
|
||||||
|
integrity sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==
|
||||||
|
dependencies:
|
||||||
|
tippy.js "^6.3.1"
|
||||||
|
|
||||||
"@tiptap/core@^2.1.13":
|
"@tiptap/core@^2.1.13":
|
||||||
version "2.1.13"
|
version "2.1.13"
|
||||||
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.13.tgz#e21f566e81688c826c6f26d2940886734189e193"
|
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.1.13.tgz#e21f566e81688c826c6f26d2940886734189e193"
|
||||||
@ -6617,16 +6624,6 @@ mkdirp@^0.5.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.6"
|
minimist "^1.2.6"
|
||||||
|
|
||||||
mobx-devtools-mst@^0.9.30:
|
|
||||||
version "0.9.30"
|
|
||||||
resolved "https://registry.yarnpkg.com/mobx-devtools-mst/-/mobx-devtools-mst-0.9.30.tgz#0d1cad8b3d97e1f3f94bb9afb701cd9c8b5b164d"
|
|
||||||
integrity sha512-6fIYeFG4xT4syIeKddmK55zQbc3ZZZr/272/cCbfaAAM5YiuFdteGZGUgdsz8wxf/mGxWZbFOM3WmASAnpwrbw==
|
|
||||||
|
|
||||||
mobx-react-devtools@^6.1.1:
|
|
||||||
version "6.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/mobx-react-devtools/-/mobx-react-devtools-6.1.1.tgz#a462b944085cf11ff96fc937d12bf31dab4c8984"
|
|
||||||
integrity sha512-nc5IXLdEUFLn3wZal65KF3/JFEFd+mbH4KTz/IG5BOPyw7jo8z29w/8qm7+wiCyqVfUIgJ1gL4+HVKmcXIOgqA==
|
|
||||||
|
|
||||||
mobx-react-lite@^4.0.3, mobx-react-lite@^4.0.4:
|
mobx-react-lite@^4.0.3, mobx-react-lite@^4.0.4:
|
||||||
version "4.0.5"
|
version "4.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-4.0.5.tgz#e2cb98f813e118917bcc463638f5bf6ea053a67b"
|
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-4.0.5.tgz#e2cb98f813e118917bcc463638f5bf6ea053a67b"
|
||||||
@ -6641,10 +6638,10 @@ mobx-react@^9.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mobx-react-lite "^4.0.4"
|
mobx-react-lite "^4.0.4"
|
||||||
|
|
||||||
mobx-state-tree@^5.4.0:
|
mobx-utils@^6.0.8:
|
||||||
version "5.4.0"
|
version "6.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-5.4.0.tgz#d41b7fd90b8d4b063bc32526758417f1100751df"
|
resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.8.tgz#843e222c7694050c2e42842682fd24a84fdb7024"
|
||||||
integrity sha512-2VuUhAqFklxgGqFNqaZUXYYSQINo8C2SUEP9YfCQrwatHWHqJLlEC7Xb+5WChkev7fubzn3aVuby26Q6h+JeBg==
|
integrity sha512-fPNt0vJnHwbQx9MojJFEnJLfM3EMGTtpy4/qOOW6xueh1mPofMajrbYAUvByMYAvCJnpy1A5L0t+ZVB5niKO4g==
|
||||||
|
|
||||||
mobx@^6.10.0:
|
mobx@^6.10.0:
|
||||||
version "6.12.0"
|
version "6.12.0"
|
||||||
@ -8540,7 +8537,7 @@ tinycolor2@^1.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
|
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
|
||||||
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
|
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
|
||||||
|
|
||||||
tippy.js@^6.3.7:
|
tippy.js@^6.3.1, tippy.js@^6.3.7:
|
||||||
version "6.3.7"
|
version "6.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
|
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
|
||||||
integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
|
integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
|
||||||
|
Loading…
Reference in New Issue
Block a user