From 54196aafc006dfc48c1512058cb50e52e1dc494e Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:46:14 +0530 Subject: [PATCH] [WEB-989] chore: archived module and cycle sidebar details (#4191) * chore: module and cycle archive * fix: archived cycle sidebar details fetch * fix: archived module sidebar details fetch --------- Co-authored-by: NarayanBavisetti --- apiserver/plane/api/urls/cycle.py | 2 +- apiserver/plane/api/views/cycle.py | 17 +- apiserver/plane/api/views/module.py | 9 +- apiserver/plane/app/urls/cycle.py | 5 + apiserver/plane/app/urls/module.py | 5 + apiserver/plane/app/views/cycle/base.py | 275 ++++++++++++--- apiserver/plane/app/views/module/base.py | 330 +++++++++++++----- web/components/cycles/cycle-peek-overview.tsx | 9 +- .../modules/module-peek-overview.tsx | 10 +- web/services/cycle_archive.service.ts | 8 + web/services/module_archive.service.ts | 8 + web/store/cycle.store.ts | 17 + web/store/module.store.ts | 17 + 13 files changed, 562 insertions(+), 150 deletions(-) diff --git a/apiserver/plane/api/urls/cycle.py b/apiserver/plane/api/urls/cycle.py index 0a775454b..b0ae21174 100644 --- a/apiserver/plane/api/urls/cycle.py +++ b/apiserver/plane/api/urls/cycle.py @@ -34,7 +34,7 @@ urlpatterns = [ name="transfer-issues", ), path( - "workspaces//projects//cycles//archive/", + "workspaces//projects//cycles//archive/", CycleArchiveUnarchiveAPIEndpoint.as_view(), name="cycle-archive-unarchive", ), diff --git a/apiserver/plane/api/views/cycle.py b/apiserver/plane/api/views/cycle.py index c2155c15e..c7df49b39 100644 --- a/apiserver/plane/api/views/cycle.py +++ b/apiserver/plane/api/views/cycle.py @@ -154,9 +154,7 @@ class CycleAPIEndpoint(WebhookMixin, BaseAPIView): data, status=status.HTTP_200_OK, ) - queryset = ( - self.get_queryset().filter(archived_at__isnull=True) - ) + queryset = self.get_queryset().filter(archived_at__isnull=True) cycle_view = request.GET.get("cycle_view", "all") # Current Cycle @@ -495,17 +493,22 @@ class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView): ).data, ) - def post(self, request, slug, project_id, pk): + def post(self, request, slug, project_id, cycle_id): cycle = Cycle.objects.get( - pk=pk, project_id=project_id, workspace__slug=slug + pk=cycle_id, project_id=project_id, workspace__slug=slug ) + if cycle.end_date >= timezone.now().date(): + return Response( + {"error": "Only completed cycles can be archived"}, + status=status.HTTP_400_BAD_REQUEST, + ) cycle.archived_at = timezone.now() cycle.save() return Response(status=status.HTTP_204_NO_CONTENT) - def delete(self, request, slug, project_id, pk): + def delete(self, request, slug, project_id, cycle_id): cycle = Cycle.objects.get( - pk=pk, project_id=project_id, workspace__slug=slug + pk=cycle_id, project_id=project_id, workspace__slug=slug ) cycle.archived_at = None cycle.save() diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 00bc3f1fe..38744eaa5 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -554,7 +554,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): .order_by(self.kwargs.get("order_by", "-created_at")) ) - def get(self, request, slug, project_id): + def get(self, request, slug, project_id, pk): return self.paginate( request=request, queryset=(self.get_queryset()), @@ -570,6 +570,13 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): module = Module.objects.get( pk=pk, project_id=project_id, workspace__slug=slug ) + if module.status not in ["completed", "cancelled"]: + return Response( + { + "error": "Only completed or cancelled modules can be archived" + }, + status=status.HTTP_400_BAD_REQUEST, + ) module.archived_at = timezone.now() module.save() return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/urls/cycle.py b/apiserver/plane/app/urls/cycle.py index 2e1779420..ce2e0f6dc 100644 --- a/apiserver/plane/app/urls/cycle.py +++ b/apiserver/plane/app/urls/cycle.py @@ -101,4 +101,9 @@ urlpatterns = [ CycleArchiveUnarchiveEndpoint.as_view(), name="cycle-archive-unarchive", ), + path( + "workspaces//projects//archived-cycles//", + CycleArchiveUnarchiveEndpoint.as_view(), + name="cycle-archive-unarchive", + ), ] diff --git a/apiserver/plane/app/urls/module.py b/apiserver/plane/app/urls/module.py index a730fcd50..bf6c84b2f 100644 --- a/apiserver/plane/app/urls/module.py +++ b/apiserver/plane/app/urls/module.py @@ -121,4 +121,9 @@ urlpatterns = [ ModuleArchiveUnarchiveEndpoint.as_view(), name="module-archive-unarchive", ), + path( + "workspaces//projects//archived-modules//", + ModuleArchiveUnarchiveEndpoint.as_view(), + name="module-archive-unarchive", + ), ] diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index b4a6250a0..58dd9891f 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -726,9 +726,20 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) ) .annotate(is_favorite=Exists(favorite_subquery)) + .annotate( + total_issues=Count( + "issue_cycle__issue__id", + distinct=True, + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) .annotate( completed_issues=Count( - "issue_cycle__issue__state__group", + "issue_cycle__issue__id", + distinct=True, filter=Q( issue_cycle__issue__state__group="completed", issue_cycle__issue__archived_at__isnull=True, @@ -738,7 +749,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( cancelled_issues=Count( - "issue_cycle__issue__state__group", + "issue_cycle__issue__id", + distinct=True, filter=Q( issue_cycle__issue__state__group="cancelled", issue_cycle__issue__archived_at__isnull=True, @@ -748,7 +760,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( started_issues=Count( - "issue_cycle__issue__state__group", + "issue_cycle__issue__id", + distinct=True, filter=Q( issue_cycle__issue__state__group="started", issue_cycle__issue__archived_at__isnull=True, @@ -758,7 +771,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( unstarted_issues=Count( - "issue_cycle__issue__state__group", + "issue_cycle__issue__id", + distinct=True, filter=Q( issue_cycle__issue__state__group="unstarted", issue_cycle__issue__archived_at__isnull=True, @@ -768,7 +782,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( backlog_issues=Count( - "issue_cycle__issue__state__group", + "issue_cycle__issue__id", + distinct=True, filter=Q( issue_cycle__issue__state__group="backlog", issue_cycle__issue__archived_at__isnull=True, @@ -802,9 +817,6 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): distinct=True, filter=~Q( issue_cycle__issue__assignees__id__isnull=True - ) - & Q( - issue_cycle__issue__assignees__member_project__is_active=True ), ), Value([], output_field=ArrayField(UUIDField())), @@ -814,53 +826,224 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): .distinct() ) - def get(self, request, slug, project_id): - queryset = ( - self.get_queryset() - .annotate( - total_issues=Count( - "issue_cycle", - filter=Q( - issue_cycle__issue__archived_at__isnull=True, - issue_cycle__issue__is_draft=False, + def get(self, request, slug, project_id, pk=None): + if pk is None: + queryset = ( + self.get_queryset() + .annotate( + total_issues=Count( + "issue_cycle", + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), + ) + ) + .values( + # necessary fields + "id", + "workspace_id", + "project_id", + # model fields + "name", + "description", + "start_date", + "end_date", + "owned_by_id", + "view_props", + "sort_order", + "external_source", + "external_id", + "progress_snapshot", + # meta fields + "total_issues", + "is_favorite", + "cancelled_issues", + "completed_issues", + "started_issues", + "unstarted_issues", + "backlog_issues", + "assignee_ids", + "status", + "archived_at", + ) + ).order_by("-is_favorite", "-created_at") + return Response(queryset, status=status.HTTP_200_OK) + else: + queryset = ( + self.get_queryset() + .filter(archived_at__isnull=False) + .filter(pk=pk) + ) + data = ( + self.get_queryset() + .filter(pk=pk) + .annotate( + sub_issues=Issue.issue_objects.filter( + project_id=self.kwargs.get("project_id"), + parent__isnull=False, + issue_cycle__cycle_id=pk, + ) + .order_by() + .annotate(count=Func(F("id"), function="Count")) + .values("count") + ) + .values( + # necessary fields + "id", + "workspace_id", + "project_id", + # model fields + "name", + "description", + "start_date", + "end_date", + "owned_by_id", + "view_props", + "sort_order", + "external_source", + "external_id", + "progress_snapshot", + "sub_issues", + # meta fields + "is_favorite", + "total_issues", + "cancelled_issues", + "completed_issues", + "started_issues", + "unstarted_issues", + "backlog_issues", + "assignee_ids", + "status", + ) + .first() + ) + queryset = queryset.first() + + if data is None: + return Response( + {"error": "Cycle does not exist"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + # Assignee Distribution + assignee_distribution = ( + Issue.objects.filter( + issue_cycle__cycle_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(first_name=F("assignees__first_name")) + .annotate(last_name=F("assignees__last_name")) + .annotate(assignee_id=F("assignees__id")) + .annotate(avatar=F("assignees__avatar")) + .annotate(display_name=F("assignees__display_name")) + .values( + "first_name", + "last_name", + "assignee_id", + "avatar", + "display_name", + ) + .annotate( + total_issues=Count( + "id", + filter=Q(archived_at__isnull=True, is_draft=False), ), ) + .annotate( + completed_issues=Count( + "id", + filter=Q( + completed_at__isnull=False, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .annotate( + pending_issues=Count( + "id", + filter=Q( + completed_at__isnull=True, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .order_by("first_name", "last_name") ) - .values( - # necessary fields - "id", - "workspace_id", - "project_id", - # model fields - "name", - "description", - "start_date", - "end_date", - "owned_by_id", - "view_props", - "sort_order", - "external_source", - "external_id", - "progress_snapshot", - # meta fields - "total_issues", - "is_favorite", - "cancelled_issues", - "completed_issues", - "started_issues", - "unstarted_issues", - "backlog_issues", - "assignee_ids", - "status", - "archived_at", + + # Label Distribution + label_distribution = ( + Issue.objects.filter( + issue_cycle__cycle_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(label_name=F("labels__name")) + .annotate(color=F("labels__color")) + .annotate(label_id=F("labels__id")) + .values("label_name", "color", "label_id") + .annotate( + total_issues=Count( + "id", + filter=Q(archived_at__isnull=True, is_draft=False), + ), + ) + .annotate( + completed_issues=Count( + "id", + filter=Q( + completed_at__isnull=False, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .annotate( + pending_issues=Count( + "id", + filter=Q( + completed_at__isnull=True, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .order_by("label_name") + ) + + data["distribution"] = { + "assignees": assignee_distribution, + "labels": label_distribution, + "completion_chart": {}, + } + + if queryset.start_date and queryset.end_date: + data["distribution"]["completion_chart"] = burndown_plot( + queryset=queryset, + slug=slug, + project_id=project_id, + cycle_id=pk, + ) + + return Response( + data, + status=status.HTTP_200_OK, ) - ).order_by("-is_favorite", "-created_at") - return Response(queryset, status=status.HTTP_200_OK) def post(self, request, slug, project_id, cycle_id): cycle = Cycle.objects.get( pk=cycle_id, project_id=project_id, workspace__slug=slug ) + + if cycle.end_date >= timezone.now().date(): + return Response( + {"error": "Only completed cycles can be archived"}, + status=status.HTTP_400_BAD_REQUEST, + ) + cycle.archived_at = timezone.now() cycle.save() return Response( diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 3fe3a078a..aaaf8fb67 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -528,13 +528,64 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): project_id=self.kwargs.get("project_id"), workspace__slug=self.kwargs.get("slug"), ) + cancelled_issues = ( + Issue.issue_objects.filter( + state__group="cancelled", + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) + completed_issues = ( + Issue.issue_objects.filter( + state__group="completed", + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) + started_issues = ( + Issue.issue_objects.filter( + state__group="started", + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) + unstarted_issues = ( + Issue.issue_objects.filter( + state__group="unstarted", + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) + backlog_issues = ( + Issue.issue_objects.filter( + state__group="backlog", + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) + total_issues = ( + Issue.issue_objects.filter( + issue_module__module_id=OuterRef("pk"), + ) + .values("issue_module__module_id") + .annotate(cnt=Count("pk")) + .values("cnt") + ) return ( Module.objects.filter(workspace__slug=self.kwargs.get("slug")) .filter(archived_at__isnull=False) .annotate(is_favorite=Exists(favorite_subquery)) - .select_related("project") - .select_related("workspace") - .select_related("lead") + .select_related("workspace", "project", "lead") .prefetch_related("members") .prefetch_related( Prefetch( @@ -545,68 +596,39 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) ) .annotate( - total_issues=Count( - "issue_module", - filter=Q( - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, - ), - ) - .annotate( - completed_issues=Count( - "issue_module__issue__state__group", - filter=Q( - issue_module__issue__state__group="completed", - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, + completed_issues=Coalesce( + Subquery(completed_issues[:1]), + Value(0, output_field=IntegerField()), ) ) .annotate( - cancelled_issues=Count( - "issue_module__issue__state__group", - filter=Q( - issue_module__issue__state__group="cancelled", - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, + cancelled_issues=Coalesce( + Subquery(cancelled_issues[:1]), + Value(0, output_field=IntegerField()), ) ) .annotate( - started_issues=Count( - "issue_module__issue__state__group", - filter=Q( - issue_module__issue__state__group="started", - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, + started_issues=Coalesce( + Subquery(started_issues[:1]), + Value(0, output_field=IntegerField()), ) ) .annotate( - unstarted_issues=Count( - "issue_module__issue__state__group", - filter=Q( - issue_module__issue__state__group="unstarted", - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, + unstarted_issues=Coalesce( + Subquery(unstarted_issues[:1]), + Value(0, output_field=IntegerField()), ) ) .annotate( - backlog_issues=Count( - "issue_module__issue__state__group", - filter=Q( - issue_module__issue__state__group="backlog", - issue_module__issue__archived_at__isnull=True, - issue_module__issue__is_draft=False, - ), - distinct=True, + backlog_issues=Coalesce( + Subquery(backlog_issues[:1]), + Value(0, output_field=IntegerField()), + ) + ) + .annotate( + total_issues=Coalesce( + Subquery(total_issues[:1]), + Value(0, output_field=IntegerField()), ) ) .annotate( @@ -622,44 +644,180 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): .order_by("-is_favorite", "-created_at") ) - def get(self, request, slug, project_id): - queryset = self.get_queryset() - modules = queryset.values( # Required fields - "id", - "workspace_id", - "project_id", - # Model fields - "name", - "description", - "description_text", - "description_html", - "start_date", - "target_date", - "status", - "lead_id", - "member_ids", - "view_props", - "sort_order", - "external_source", - "external_id", - # computed fields - "total_issues", - "is_favorite", - "cancelled_issues", - "completed_issues", - "started_issues", - "unstarted_issues", - "backlog_issues", - "created_at", - "updated_at", - "archived_at", - ) - return Response(modules, status=status.HTTP_200_OK) + def get(self, request, slug, project_id, pk=None): + if pk is None: + queryset = self.get_queryset() + modules = queryset.values( # Required fields + "id", + "workspace_id", + "project_id", + # Model fields + "name", + "description", + "description_text", + "description_html", + "start_date", + "target_date", + "status", + "lead_id", + "member_ids", + "view_props", + "sort_order", + "external_source", + "external_id", + # computed fields + "total_issues", + "is_favorite", + "cancelled_issues", + "completed_issues", + "started_issues", + "unstarted_issues", + "backlog_issues", + "created_at", + "updated_at", + "archived_at", + ) + return Response(modules, status=status.HTTP_200_OK) + else: + queryset = ( + self.get_queryset() + .filter(pk=pk) + .annotate( + sub_issues=Issue.issue_objects.filter( + project_id=self.kwargs.get("project_id"), + parent__isnull=False, + issue_module__module_id=pk, + ) + .order_by() + .annotate(count=Func(F("id"), function="Count")) + .values("count") + ) + ) + assignee_distribution = ( + Issue.objects.filter( + issue_module__module_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(first_name=F("assignees__first_name")) + .annotate(last_name=F("assignees__last_name")) + .annotate(assignee_id=F("assignees__id")) + .annotate(display_name=F("assignees__display_name")) + .annotate(avatar=F("assignees__avatar")) + .values( + "first_name", + "last_name", + "assignee_id", + "avatar", + "display_name", + ) + .annotate( + total_issues=Count( + "id", + filter=Q( + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .annotate( + completed_issues=Count( + "id", + filter=Q( + completed_at__isnull=False, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .annotate( + pending_issues=Count( + "id", + filter=Q( + completed_at__isnull=True, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .order_by("first_name", "last_name") + ) + + label_distribution = ( + Issue.objects.filter( + issue_module__module_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(label_name=F("labels__name")) + .annotate(color=F("labels__color")) + .annotate(label_id=F("labels__id")) + .values("label_name", "color", "label_id") + .annotate( + total_issues=Count( + "id", + filter=Q( + archived_at__isnull=True, + is_draft=False, + ), + ), + ) + .annotate( + completed_issues=Count( + "id", + filter=Q( + completed_at__isnull=False, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .annotate( + pending_issues=Count( + "id", + filter=Q( + completed_at__isnull=True, + archived_at__isnull=True, + is_draft=False, + ), + ) + ) + .order_by("label_name") + ) + + data = ModuleDetailSerializer(queryset.first()).data + data["distribution"] = { + "assignees": assignee_distribution, + "labels": label_distribution, + "completion_chart": {}, + } + + # Fetch the modules + modules = queryset.first() + if modules and modules.start_date and modules.target_date: + data["distribution"]["completion_chart"] = burndown_plot( + queryset=modules, + slug=slug, + project_id=project_id, + module_id=pk, + ) + + return Response( + data, + status=status.HTTP_200_OK, + ) def post(self, request, slug, project_id, module_id): module = Module.objects.get( pk=module_id, project_id=project_id, workspace__slug=slug ) + if module.status not in ["completed", "cancelled"]: + return Response( + { + "error": "Only completed or cancelled modules can be archived" + }, + status=status.HTTP_400_BAD_REQUEST, + ) module.archived_at = timezone.now() module.save() return Response( diff --git a/web/components/cycles/cycle-peek-overview.tsx b/web/components/cycles/cycle-peek-overview.tsx index 8409c06fe..8c6d6ae5f 100644 --- a/web/components/cycles/cycle-peek-overview.tsx +++ b/web/components/cycles/cycle-peek-overview.tsx @@ -19,7 +19,7 @@ export const CyclePeekOverview: React.FC = observer(({ projectId, workspa // refs const ref = React.useRef(null); // store hooks - const { fetchCycleDetails } = useCycle(); + const { fetchCycleDetails, fetchArchivedCycleDetails } = useCycle(); const handleClose = () => { delete router.query.peekCycle; @@ -30,9 +30,10 @@ export const CyclePeekOverview: React.FC = observer(({ projectId, workspa }; useEffect(() => { - if (!peekCycle || isArchived) return; - fetchCycleDetails(workspaceSlug, projectId, peekCycle.toString()); - }, [fetchCycleDetails, isArchived, peekCycle, projectId, workspaceSlug]); + if (!peekCycle) return; + if (isArchived) fetchArchivedCycleDetails(workspaceSlug, projectId, peekCycle.toString()); + else fetchCycleDetails(workspaceSlug, projectId, peekCycle.toString()); + }, [fetchArchivedCycleDetails, fetchCycleDetails, isArchived, peekCycle, projectId, workspaceSlug]); return ( <> diff --git a/web/components/modules/module-peek-overview.tsx b/web/components/modules/module-peek-overview.tsx index ba470a649..7af605093 100644 --- a/web/components/modules/module-peek-overview.tsx +++ b/web/components/modules/module-peek-overview.tsx @@ -19,7 +19,7 @@ export const ModulePeekOverview: React.FC = observer(({ projectId, worksp // refs const ref = React.useRef(null); // store hooks - const { fetchModuleDetails } = useModule(); + const { fetchModuleDetails, fetchArchivedModuleDetails } = useModule(); const handleClose = () => { delete router.query.peekModule; @@ -30,10 +30,10 @@ export const ModulePeekOverview: React.FC = observer(({ projectId, worksp }; useEffect(() => { - if (!peekModule || isArchived) return; - - fetchModuleDetails(workspaceSlug, projectId, peekModule.toString()); - }, [fetchModuleDetails, isArchived, peekModule, projectId, workspaceSlug]); + if (!peekModule) return; + if (isArchived) fetchArchivedModuleDetails(workspaceSlug, projectId, peekModule.toString()); + else fetchModuleDetails(workspaceSlug, projectId, peekModule.toString()); + }, [fetchArchivedModuleDetails, fetchModuleDetails, isArchived, peekModule, projectId, workspaceSlug]); return ( <> diff --git a/web/services/cycle_archive.service.ts b/web/services/cycle_archive.service.ts index 6ea3aeb40..06f3f426a 100644 --- a/web/services/cycle_archive.service.ts +++ b/web/services/cycle_archive.service.ts @@ -18,6 +18,14 @@ export class CycleArchiveService extends APIService { }); } + async getArchivedCycleDetails(workspaceSlug: string, projectId: string, cycleId: string): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-cycles/${cycleId}/`) + .then((res) => res?.data) + .catch((err) => { + throw err?.response?.data; + }); + } + async archiveCycle( workspaceSlug: string, projectId: string, diff --git a/web/services/module_archive.service.ts b/web/services/module_archive.service.ts index 3b8f05827..c74f4c640 100644 --- a/web/services/module_archive.service.ts +++ b/web/services/module_archive.service.ts @@ -18,6 +18,14 @@ export class ModuleArchiveService extends APIService { }); } + async getArchivedModuleDetails(workspaceSlug: string, projectId: string, moduleId: string): Promise { + return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-modules/${moduleId}/`) + .then((response) => response?.data) + .catch((error) => { + throw error?.response?.data; + }); + } + async archiveModule( workspaceSlug: string, projectId: string, diff --git a/web/store/cycle.store.ts b/web/store/cycle.store.ts index 18bc58dc7..f56e9f47e 100644 --- a/web/store/cycle.store.ts +++ b/web/store/cycle.store.ts @@ -46,6 +46,7 @@ export interface ICycleStore { fetchAllCycles: (workspaceSlug: string, projectId: string) => Promise; fetchActiveCycle: (workspaceSlug: string, projectId: string) => Promise; fetchArchivedCycles: (workspaceSlug: string, projectId: string) => Promise; + fetchArchivedCycleDetails: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; fetchCycleDetails: (workspaceSlug: string, projectId: string, cycleId: string) => Promise; // crud createCycle: (workspaceSlug: string, projectId: string, data: Partial) => Promise; @@ -99,6 +100,7 @@ export class CycleStore implements ICycleStore { fetchAllCycles: action, fetchActiveCycle: action, fetchArchivedCycles: action, + fetchArchivedCycleDetails: action, fetchCycleDetails: action, createCycle: action, updateCycleDetails: action, @@ -415,6 +417,21 @@ export class CycleStore implements ICycleStore { return response; }); + /** + * @description fetches cycle details + * @param workspaceSlug + * @param projectId + * @param cycleId + * @returns + */ + fetchArchivedCycleDetails = async (workspaceSlug: string, projectId: string, cycleId: string) => + await this.cycleArchiveService.getArchivedCycleDetails(workspaceSlug, projectId, cycleId).then((response) => { + runInAction(() => { + set(this.cycleMap, [response.id], { ...this.cycleMap?.[response.id], ...response }); + }); + return response; + }); + /** * @description fetches cycle details * @param workspaceSlug diff --git a/web/store/module.store.ts b/web/store/module.store.ts index fd180545c..3ab645504 100644 --- a/web/store/module.store.ts +++ b/web/store/module.store.ts @@ -33,6 +33,7 @@ export interface IModuleStore { fetchWorkspaceModules: (workspaceSlug: string) => Promise; fetchModules: (workspaceSlug: string, projectId: string) => Promise; fetchArchivedModules: (workspaceSlug: string, projectId: string) => Promise; + fetchArchivedModuleDetails: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; fetchModuleDetails: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; // crud createModule: (workspaceSlug: string, projectId: string, data: Partial) => Promise; @@ -91,6 +92,7 @@ export class ModulesStore implements IModuleStore { fetchWorkspaceModules: action, fetchModules: action, fetchArchivedModules: action, + fetchArchivedModuleDetails: action, fetchModuleDetails: action, createModule: action, updateModuleDetails: action, @@ -274,6 +276,21 @@ export class ModulesStore implements IModuleStore { }); }; + /** + * @description fetch module details + * @param workspaceSlug + * @param projectId + * @param moduleId + * @returns IModule + */ + fetchArchivedModuleDetails = async (workspaceSlug: string, projectId: string, moduleId: string) => + await this.moduleArchiveService.getArchivedModuleDetails(workspaceSlug, projectId, moduleId).then((response) => { + runInAction(() => { + set(this.moduleMap, [moduleId], response); + }); + return response; + }); + /** * @description fetch module details * @param workspaceSlug