diff --git a/README.md b/README.md index ece8ff1e2f..38ead5f997 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

Plane

-

Open-source project management that unlocks customer value.

+

Open-source project management that unlocks customer value

@@ -40,22 +40,22 @@

-Meet [Plane](https://dub.sh/plane-website-readme). An open-source software development tool to manage issues, sprints, and product roadmaps with peace of mind. 🧘‍♀️ +Meet [Plane](https://dub.sh/plane-website-readme), an open-source project management tool to track issues, run ~sprints~ cycles, and manage product roadmaps without the chaos of managing the tool itself. 🧘‍♀️ -> Plane is still in its early days, not everything will be perfect yet, and hiccups may happen. Please let us know of any suggestions, ideas, or bugs that you encounter on our [Discord](https://discord.com/invite/A92xrEGCge) or GitHub issues, and we will use your feedback to improve in our upcoming releases. +> Plane is evolving every day. Your suggestions, ideas, and reported bugs help us immensely. Do not hesitate to join in the conversation on [Discord](https://discord.com/invite/A92xrEGCge) or raise a GitHub issue. We read everything and respond to most. ## ⚡ Installation -The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account where we offer a hosted solution for users. +The easiest way to get started with Plane is by creating a [Plane Cloud](https://app.plane.so) account. -If you want more control over your data, prefer to self-host Plane, please refer to our [deployment documentation](https://docs.plane.so/docker-compose). +If you would like to self-host Plane, please see our [deployment guide](https://docs.plane.so/docker-compose). -| Installation Methods | Documentation Link | +| Installation methods | Docs link | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Docker | [![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white)](https://docs.plane.so/self-hosting/methods/docker-compose) | | Kubernetes | [![Kubernetes](https://img.shields.io/badge/kubernetes-%23326ce5.svg?style=for-the-badge&logo=kubernetes&logoColor=white)](https://docs.plane.so/kubernetes) | -`Instance admin` can configure instance settings using our [God-mode](https://docs.plane.so/instance-admin) feature. +`Instance admins` can configure instance settings with [God-mode](https://docs.plane.so/instance-admin). ## 🚀 Features diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index b884d60a3b..748af96fb9 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -636,6 +636,7 @@ class IssueInboxSerializer(DynamicBaseSerializer): "project_id", "created_at", "label_ids", + "created_by", ] read_only_fields = fields diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 5a987dad87..4dc4c49218 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -431,15 +431,15 @@ class ModuleViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, pk): module = self.get_queryset().filter(pk=pk) - current_instance = json.dumps( - ModuleSerializer(module).data, cls=DjangoJSONEncoder - ) if module.first().archived_at: return Response( {"error": "Archived module cannot be updated"}, status=status.HTTP_400_BAD_REQUEST, ) + current_instance = json.dumps( + ModuleSerializer(module.first()).data, cls=DjangoJSONEncoder + ) serializer = ModuleWriteSerializer( module.first(), data=request.data, partial=True ) diff --git a/apiserver/plane/bgtasks/webhook_task.py b/apiserver/plane/bgtasks/webhook_task.py index d1e1cb34c8..6696a569c1 100644 --- a/apiserver/plane/bgtasks/webhook_task.py +++ b/apiserver/plane/bgtasks/webhook_task.py @@ -15,6 +15,7 @@ from django.core.mail import EmailMultiAlternatives, get_connection from django.core.serializers.json import DjangoJSONEncoder from django.template.loader import render_to_string from django.utils.html import strip_tags +from django.core.exceptions import ObjectDoesNotExist # Module imports from plane.api.serializers import ( @@ -422,6 +423,9 @@ def webhook_activity( ) return except Exception as e: + # Return if a does not exist error occurs + if isinstance(e, ObjectDoesNotExist): + return if settings.DEBUG: print(e) log_exception(e) @@ -462,21 +466,23 @@ def model_activity( # Loop through all keys in requested data and check the current value and requested value for key in requested_data: - current_value = current_instance.get(key, None) - requested_value = requested_data.get(key, None) - if current_value != requested_value: - webhook_activity.delay( - event=model_name, - verb="updated", - field=key, - old_value=current_value, - new_value=requested_value, - actor_id=actor_id, - slug=slug, - current_site=origin, - event_id=model_id, - old_identifier=None, - new_identifier=None, - ) + # Check if key is present in current instance or not + if key in current_instance: + current_value = current_instance.get(key, None) + requested_value = requested_data.get(key, None) + if current_value != requested_value: + webhook_activity.delay( + event=model_name, + verb="updated", + field=key, + old_value=current_value, + new_value=requested_value, + actor_id=actor_id, + slug=slug, + current_site=origin, + event_id=model_id, + old_identifier=None, + new_identifier=None, + ) return diff --git a/packages/editor/core/src/ui/plugins/upload-image.tsx b/packages/editor/core/src/ui/plugins/upload-image.tsx index af56d53823..7a370da4e3 100644 --- a/packages/editor/core/src/ui/plugins/upload-image.tsx +++ b/packages/editor/core/src/ui/plugins/upload-image.tsx @@ -158,7 +158,7 @@ export async function startImageUpload( const transaction = view.state.tr.insert(pos - 1, node).setMeta(uploadKey, { remove: { id } }); view.dispatch(transaction); - view.focus(); + if (view.hasFocus()) view.focus(); editor.storage.image.uploadInProgress = false; } catch (error) { removePlaceholder(editor, view, id); diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index d1778e31aa..275506ad73 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -66,6 +66,11 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); useOutsideClickDetector(dropdownRef, closeDropdown); + const toggleDropdown = () => { + if (isOpen) closeDropdown(); + else openDropdown(); + }; + return ( { ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80" } ${customButtonClassName}`} - onClick={openDropdown} + onClick={toggleDropdown} > {customButton} @@ -107,7 +112,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80" } ${buttonClassName}`} - onClick={openDropdown} + onClick={toggleDropdown} > {label} {!noChevron && !disabled &&