From 7404fe71b1808dfd353169463d650f7db91c76ec Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Wed, 27 Sep 2023 17:30:52 +0530 Subject: [PATCH 01/11] chore: sort order and issue props for global views (#2283) --- ...er_workspacemember_issue_props_and_more.py | 24 +++++++++++++++++++ apiserver/plane/db/models/view.py | 11 +++++++++ apiserver/plane/db/models/workspace.py | 12 +++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 apiserver/plane/db/migrations/0048_globalview_sort_order_workspacemember_issue_props_and_more.py diff --git a/apiserver/plane/db/migrations/0048_globalview_sort_order_workspacemember_issue_props_and_more.py b/apiserver/plane/db/migrations/0048_globalview_sort_order_workspacemember_issue_props_and_more.py new file mode 100644 index 000000000..3084ef637 --- /dev/null +++ b/apiserver/plane/db/migrations/0048_globalview_sort_order_workspacemember_issue_props_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2023-09-27 11:18 + +from django.db import migrations, models +import plane.db.models.workspace + + +class Migration(migrations.Migration): + + dependencies = [ + ('db', '0047_auto_20230926_1029'), + ] + + operations = [ + migrations.AddField( + model_name='globalview', + name='sort_order', + field=models.FloatField(default=65535), + ), + migrations.AddField( + model_name='workspacemember', + name='issue_props', + field=models.JSONField(default=plane.db.models.workspace.get_issue_props), + ), + ] diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index 6e0a47105..c19e444b3 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -17,12 +17,23 @@ class GlobalView(BaseModel): default=1, choices=((0, "Private"), (1, "Public")) ) query_data = models.JSONField(default=dict) + sort_order = models.FloatField(default=65535) class Meta: verbose_name = "Global View" verbose_name_plural = "Global Views" db_table = "global_views" ordering = ("-created_at",) + + def save(self, *args, **kwargs): + if self._state.adding: + largest_sort_order = GlobalView.objects.filter( + project=self.project + ).aggregate(largest=models.Max("sort_order"))["largest"] + if largest_sort_order is not None: + self.sort_order = largest_sort_order + 10000 + + super(GlobalView, self).save(*args, **kwargs) def __str__(self): """Return name of the View""" diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index c85268435..d1012f549 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -29,7 +29,7 @@ def get_default_props(): }, "display_filters": { "group_by": None, - "order_by": '-created_at', + "order_by": "-created_at", "type": None, "sub_issue": True, "show_empty_groups": True, @@ -54,6 +54,15 @@ def get_default_props(): } +def get_issue_props(): + return { + "subscribed": True, + "assigned": True, + "created": True, + "all_issues": True, + } + + class Workspace(BaseModel): name = models.CharField(max_length=80, verbose_name="Workspace Name") logo = models.URLField(verbose_name="Logo", blank=True, null=True) @@ -89,6 +98,7 @@ class WorkspaceMember(BaseModel): company_role = models.TextField(null=True, blank=True) view_props = models.JSONField(default=get_default_props) default_props = models.JSONField(default=get_default_props) + issue_props = models.JSONField(default=get_issue_props) class Meta: unique_together = ["workspace", "member"] From 9dd22f07f40f60ee764f87e7494403110e92bb22 Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:16:22 +0530 Subject: [PATCH 02/11] chore: removed project filter (#2284) --- apiserver/plane/db/models/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index c19e444b3..44bc994d0 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -28,7 +28,7 @@ class GlobalView(BaseModel): def save(self, *args, **kwargs): if self._state.adding: largest_sort_order = GlobalView.objects.filter( - project=self.project + workspace=self.workspace ).aggregate(largest=models.Max("sort_order"))["largest"] if largest_sort_order is not None: self.sort_order = largest_sort_order + 10000 From 32d2f912f7ad719ecfaa1a02d3cf1fdb56123019 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:14:37 +0530 Subject: [PATCH 03/11] fix: inbox issue deletes (#2290) --- apiserver/plane/api/views/inbox.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apiserver/plane/api/views/inbox.py b/apiserver/plane/api/views/inbox.py index 79294275e..4bfc32f01 100644 --- a/apiserver/plane/api/views/inbox.py +++ b/apiserver/plane/api/views/inbox.py @@ -370,6 +370,11 @@ class InboxIssueViewSet(BaseViewSet): if project_member.role <= 10 and str(inbox_issue.created_by_id) != str(request.user.id): return Response({"error": "You cannot delete inbox issue"}, status=status.HTTP_400_BAD_REQUEST) + # Check the issue status + if inbox_issue.status in [-2, -1, 0, 2]: + # Delete the issue also + Issue.objects.filter(workspace__slug=slug, project_id=project_id, pk=inbox_issue.issue_id).delete() + inbox_issue.delete() return Response(status=status.HTTP_204_NO_CONTENT) except InboxIssue.DoesNotExist: From 6afbd3f1ba434a022eeae92e96c49e784243f961 Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:16:31 +0530 Subject: [PATCH 04/11] chore: views (#2288) * chore: global views order by * chore: update permissions for global views --------- Co-authored-by: NarayanBavisetti --- apiserver/plane/api/permissions/workspace.py | 11 ++++++++++- apiserver/plane/api/views/issue.py | 2 -- apiserver/plane/api/views/view.py | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apiserver/plane/api/permissions/workspace.py b/apiserver/plane/api/permissions/workspace.py index d01b545ee..66e836614 100644 --- a/apiserver/plane/api/permissions/workspace.py +++ b/apiserver/plane/api/permissions/workspace.py @@ -58,8 +58,17 @@ class WorkspaceEntityPermission(BasePermission): if request.user.is_anonymous: return False + ## Safe Methods -> Handle the filtering logic in queryset + if request.method in SAFE_METHODS: + return WorkspaceMember.objects.filter( + workspace__slug=view.workspace_slug, + member=request.user, + ).exists() + return WorkspaceMember.objects.filter( - member=request.user, workspace__slug=view.workspace_slug + member=request.user, + workspace__slug=view.workspace_slug, + role__in=[Owner, Admin], ).exists() diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 844095434..29f14e437 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -24,7 +24,6 @@ from django.core.serializers.json import DjangoJSONEncoder from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page from django.db import IntegrityError -from django.conf import settings from django.db import IntegrityError # Third Party imports @@ -58,7 +57,6 @@ from plane.api.serializers import ( IssuePublicSerializer, ) from plane.api.permissions import ( - WorkspaceEntityPermission, ProjectEntityPermission, WorkSpaceAdminPermission, ProjectMemberPermission, diff --git a/apiserver/plane/api/views/view.py b/apiserver/plane/api/views/view.py index b6f1d7c4b..435f8725a 100644 --- a/apiserver/plane/api/views/view.py +++ b/apiserver/plane/api/views/view.py @@ -61,7 +61,7 @@ class GlobalViewViewSet(BaseViewSet): .get_queryset() .filter(workspace__slug=self.kwargs.get("slug")) .select_related("workspace") - .order_by("-created_at") + .order_by(self.request.GET.get("order_by", "-created_at")) .distinct() ) From 34af666b5fbed6a6dd49292b6849ce6f1ea44d3c Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:17:46 +0530 Subject: [PATCH 05/11] chore: fetch issues from previous and next month in the calendar view (#2282) --- web/hooks/use-calendar-issues-view.tsx | 28 ++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/web/hooks/use-calendar-issues-view.tsx b/web/hooks/use-calendar-issues-view.tsx index 1f6f04ea8..5f21d63c5 100644 --- a/web/hooks/use-calendar-issues-view.tsx +++ b/web/hooks/use-calendar-issues-view.tsx @@ -33,8 +33,28 @@ const useCalendarIssuesView = () => { const [activeMonthDate, setActiveMonthDate] = useState(new Date()); - const firstDayOfMonth = new Date(activeMonthDate.getFullYear(), activeMonthDate.getMonth(), 1); - const lastDayOfMonth = new Date(activeMonthDate.getFullYear(), activeMonthDate.getMonth() + 1, 0); + // previous month's first date + const previousMonthYear = + activeMonthDate.getMonth() === 0 + ? activeMonthDate.getFullYear() - 1 + : activeMonthDate.getFullYear(); + const previousMonthMonth = activeMonthDate.getMonth() === 0 ? 11 : activeMonthDate.getMonth() - 1; + + const previousMonthFirstDate = new Date(previousMonthYear, previousMonthMonth, 1); + + // next month's last date + const nextMonthYear = + activeMonthDate.getMonth() === 11 + ? activeMonthDate.getFullYear() + 1 + : activeMonthDate.getFullYear(); + const nextMonthMonth = (activeMonthDate.getMonth() + 1) % 12; + const nextMonthFirstDate = new Date(nextMonthYear, nextMonthMonth, 1); + + const nextMonthLastDate = new Date( + nextMonthFirstDate.getFullYear(), + nextMonthFirstDate.getMonth() + 1, + 0 + ); const router = useRouter(); const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; @@ -47,8 +67,8 @@ const useCalendarIssuesView = () => { labels: filters?.labels ? filters?.labels.join(",") : undefined, created_by: filters?.created_by ? filters?.created_by.join(",") : undefined, start_date: filters?.start_date ? filters?.start_date.join(",") : undefined, - target_date: `${renderDateFormat(firstDayOfMonth)};after,${renderDateFormat( - lastDayOfMonth + target_date: `${renderDateFormat(previousMonthFirstDate)};after,${renderDateFormat( + nextMonthLastDate )};before`, }; From 60a69e28e3eb0ea3528df6c1c02ea94dd81f2f91 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:18:35 +0530 Subject: [PATCH 06/11] fix: issue activity estimate value bug fix (#2281) * fix: issue activity estimate value bug fix * fix: activity typo fix --- web/components/core/activity.tsx | 17 +++++++++++++++-- web/hooks/use-estimate-option.tsx | 4 +++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index c76f1aece..4e01c5ed8 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -2,6 +2,8 @@ import { useRouter } from "next/router"; import useSWR from "swr"; +// hook +import useEstimateOption from "hooks/use-estimate-option"; // services import issuesService from "services/issues.service"; // icons @@ -77,6 +79,18 @@ const LabelPill = ({ labelId }: { labelId: string }) => { /> ); }; +const EstimatePoint = ({ point }: { point: string }) => { + const { estimateValue, isEstimateActive } = useEstimateOption(Number(point)); + const currentPoint = Number(point) + 1; + + return ( + + {isEstimateActive + ? estimateValue + : `${currentPoint} ${currentPoint > 1 ? "points" : "point"}`} + + ); +}; const activityDetails: { [key: string]: { @@ -324,8 +338,7 @@ const activityDetails: { else return ( <> - set the estimate point to{" "} - {activity.new_value} + set the estimate point to {showIssue && ( <> {" "} diff --git a/web/hooks/use-estimate-option.tsx b/web/hooks/use-estimate-option.tsx index 37b42b9e9..61a93ca59 100644 --- a/web/hooks/use-estimate-option.tsx +++ b/web/hooks/use-estimate-option.tsx @@ -32,7 +32,9 @@ const useEstimateOption = (estimateKey?: number | null) => { ); const estimateValue: any = - (estimateKey && estimateDetails?.points?.find((e) => e.key === estimateKey)?.value) ?? "None"; + estimateKey || estimateKey === 0 + ? estimateDetails?.points?.find((e) => e.key === estimateKey)?.value + : "None"; return { isEstimateActive: projectDetails?.estimate ? true : false, From ec91a0d2e50ec91b93ca7f8f79d1e23086621c86 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:02:03 +0530 Subject: [PATCH 07/11] fix: ui and bugs (#2289) * fix: 24 character limit on first & last name in onboarding page * fix: no option: 'Add Issue' in archive issue page * fix: in archive issue directly sending to issue detail page * fix: issue type showing in archive issue * fix: custom menu overflowing * fix: changing subscriber in filters has no effect * style: border in quick-add * fix: on onboarding member role overflowing * fix: inconsistent icons in issue detail * style: spacing, borders and shadows in quick-add * fix: custom menu truncate --- web/components/core/activity.tsx | 84 ++++--- web/components/core/filters/filters-list.tsx | 4 +- .../core/filters/issues-view-filter.tsx | 54 +++-- .../board-view/inline-create-issue-form.tsx | 2 +- .../inline-create-issue-form.tsx | 4 +- .../inline-create-issue-form.tsx | 2 +- web/components/core/views/issues-view.tsx | 5 +- .../list-view/inline-create-issue-form.tsx | 4 +- .../core/views/list-view/single-issue.tsx | 3 +- .../core/views/list-view/single-list.tsx | 5 +- .../assignee-column/assignee-column.tsx | 2 +- .../created-on-column/created-on-column.tsx | 2 +- .../due-date-column/due-date-column.tsx | 2 +- .../estimate-column/estimate-column.tsx | 2 +- .../issue-column/issue-column.tsx | 2 +- .../label-column/label-column.tsx | 2 +- .../priority-column/priority-column.tsx | 2 +- .../spreadsheet-view/spreadsheet-view.tsx | 10 +- .../start-date-column/start-date-column.tsx | 2 +- .../state-column/state-column.tsx | 2 +- .../updated-on-column/updated-on-column.tsx | 2 +- web/components/gantt-chart/chart/index.tsx | 2 +- web/components/onboarding/invite-members.tsx | 225 ++++++++++++------ web/components/onboarding/user-details.tsx | 8 + .../profile/profile-issues-view-options.tsx | 8 +- web/constants/fetch-keys.ts | 5 +- web/hooks/use-issues-view.tsx | 2 +- 27 files changed, 289 insertions(+), 158 deletions(-) diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index 4e01c5ed8..d5987384c 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -8,9 +8,27 @@ import useEstimateOption from "hooks/use-estimate-option"; import issuesService from "services/issues.service"; // icons import { Icon, Tooltip } from "components/ui"; -import { CopyPlus } from "lucide-react"; -import { Squares2X2Icon } from "@heroicons/react/24/outline"; -import { BlockedIcon, BlockerIcon, RelatedIcon } from "components/icons"; +import { + TagIcon, + CopyPlus, + Calendar, + Link2Icon, + RocketIcon, + Users2Icon, + ArchiveIcon, + PaperclipIcon, + ContrastIcon, + TriangleIcon, + LayoutGridIcon, + SignalMediumIcon, + MessageSquareIcon, +} from "lucide-react"; +import { + BlockedIcon, + BlockerIcon, + RelatedIcon, + StackedLayersHorizontalIcon, +} from "components/icons"; // helpers import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { capitalizeFirstLetter } from "helpers/string.helper"; @@ -38,7 +56,7 @@ const IssueLink = ({ activity }: { activity: IIssueActivity }) => { {activity.issue_detail ? `${activity.project_detail.identifier}-${activity.issue_detail.sequence_id}` : "Issue"} - + ); @@ -131,14 +149,14 @@ const activityDetails: { ); }, - icon: