mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 17:18:33 +00:00
Order workflow actions
This commit is contained in:
parent
947a1e342b
commit
242e8359cd
31 changed files with 593 additions and 53 deletions
backend
src/baserow
api/workflow_actions
contrib/builder
core/workflow_actions
test_utils/fixtures
tests/baserow/contrib/builder
enterprise/backend/src/baserow_enterprise/role
web-frontend/modules
builder
core
assets/scss/components
components/workflowActions
|
@ -12,7 +12,7 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WorkflowAction
|
model = WorkflowAction
|
||||||
fields = ("id", "type")
|
fields = ("id", "order", "type")
|
||||||
|
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"id": {"read_only": True},
|
"id": {"read_only": True},
|
||||||
|
|
|
@ -5,3 +5,9 @@ ERROR_WORKFLOW_ACTION_DOES_NOT_EXIST = (
|
||||||
HTTP_404_NOT_FOUND,
|
HTTP_404_NOT_FOUND,
|
||||||
"The requested workflow action does not exist.",
|
"The requested workflow action does not exist.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ERROR_WORKFLOW_ACTION_NOT_IN_ELEMENT = (
|
||||||
|
"ERROR_WORKFLOW_ACTION_NOT_IN_ELEMENT",
|
||||||
|
HTTP_404_NOT_FOUND,
|
||||||
|
"The requested workflow action does not belong to the element",
|
||||||
|
)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class BuilderWorkflowActionSerializer(WorkflowActionSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BuilderWorkflowAction
|
model = BuilderWorkflowAction
|
||||||
fields = ("id", "element_id", "type", "event")
|
fields = ("id", "order", "element_id", "type", "event")
|
||||||
|
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"id": {"read_only": True},
|
"id": {"read_only": True},
|
||||||
|
@ -59,3 +59,14 @@ class UpdateBuilderWorkflowActionsSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BuilderWorkflowAction
|
model = BuilderWorkflowAction
|
||||||
fields = ("type",)
|
fields = ("type",)
|
||||||
|
|
||||||
|
|
||||||
|
class OrderWorkflowActionsSerializer(serializers.Serializer):
|
||||||
|
workflow_action_ids = serializers.ListField(
|
||||||
|
child=serializers.IntegerField(),
|
||||||
|
help_text="The ids of the workflow actions in the order they are supposed to be "
|
||||||
|
"set in",
|
||||||
|
)
|
||||||
|
element_id = serializers.IntegerField(
|
||||||
|
required=False, help_text="The element the workflow actions belong to"
|
||||||
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.urls import re_path
|
||||||
from baserow.contrib.builder.api.workflow_actions.views import (
|
from baserow.contrib.builder.api.workflow_actions.views import (
|
||||||
BuilderWorkflowActionsView,
|
BuilderWorkflowActionsView,
|
||||||
BuilderWorkflowActionView,
|
BuilderWorkflowActionView,
|
||||||
|
OrderBuilderWorkflowActionsView,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_name = "baserow.contrib.builder.api.workflow_actions"
|
app_name = "baserow.contrib.builder.api.workflow_actions"
|
||||||
|
@ -18,4 +19,9 @@ urls_without_builder_id = [
|
||||||
BuilderWorkflowActionView.as_view(),
|
BuilderWorkflowActionView.as_view(),
|
||||||
name="item",
|
name="item",
|
||||||
),
|
),
|
||||||
|
re_path(
|
||||||
|
r"page/(?P<page_id>[0-9]+)/workflow_actions/order/$",
|
||||||
|
OrderBuilderWorkflowActionsView.as_view(),
|
||||||
|
name="order",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,7 +8,7 @@ from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from baserow.api.decorators import validate_body_custom_fields
|
from baserow.api.decorators import validate_body, validate_body_custom_fields
|
||||||
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
|
from baserow.api.schemas import CLIENT_SESSION_ID_SCHEMA_PARAMETER, get_error_schema
|
||||||
from baserow.api.utils import (
|
from baserow.api.utils import (
|
||||||
CustomFieldRegistryMappingSerializer,
|
CustomFieldRegistryMappingSerializer,
|
||||||
|
@ -21,15 +21,21 @@ from baserow.contrib.builder.api.elements.errors import ERROR_ELEMENT_DOES_NOT_E
|
||||||
from baserow.contrib.builder.api.pages.errors import ERROR_PAGE_DOES_NOT_EXIST
|
from baserow.contrib.builder.api.pages.errors import ERROR_PAGE_DOES_NOT_EXIST
|
||||||
from baserow.contrib.builder.api.workflow_actions.errors import (
|
from baserow.contrib.builder.api.workflow_actions.errors import (
|
||||||
ERROR_WORKFLOW_ACTION_DOES_NOT_EXIST,
|
ERROR_WORKFLOW_ACTION_DOES_NOT_EXIST,
|
||||||
|
ERROR_WORKFLOW_ACTION_NOT_IN_ELEMENT,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.api.workflow_actions.serializers import (
|
from baserow.contrib.builder.api.workflow_actions.serializers import (
|
||||||
BuilderWorkflowActionSerializer,
|
BuilderWorkflowActionSerializer,
|
||||||
CreateBuilderWorkflowActionSerializer,
|
CreateBuilderWorkflowActionSerializer,
|
||||||
|
OrderWorkflowActionsSerializer,
|
||||||
UpdateBuilderWorkflowActionsSerializer,
|
UpdateBuilderWorkflowActionsSerializer,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.elements.exceptions import ElementDoesNotExist
|
from baserow.contrib.builder.elements.exceptions import ElementDoesNotExist
|
||||||
|
from baserow.contrib.builder.elements.handler import ElementHandler
|
||||||
from baserow.contrib.builder.pages.exceptions import PageDoesNotExist
|
from baserow.contrib.builder.pages.exceptions import PageDoesNotExist
|
||||||
from baserow.contrib.builder.pages.handler import PageHandler
|
from baserow.contrib.builder.pages.handler import PageHandler
|
||||||
|
from baserow.contrib.builder.workflow_actions.exceptions import (
|
||||||
|
WorkflowActionNotInElement,
|
||||||
|
)
|
||||||
from baserow.contrib.builder.workflow_actions.handler import (
|
from baserow.contrib.builder.workflow_actions.handler import (
|
||||||
BuilderWorkflowActionHandler,
|
BuilderWorkflowActionHandler,
|
||||||
)
|
)
|
||||||
|
@ -257,3 +263,58 @@ class BuilderWorkflowActionView(APIView):
|
||||||
workflow_action_updated, BuilderWorkflowActionSerializer
|
workflow_action_updated, BuilderWorkflowActionSerializer
|
||||||
)
|
)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
class OrderBuilderWorkflowActionsView(APIView):
|
||||||
|
@extend_schema(
|
||||||
|
parameters=[
|
||||||
|
OpenApiParameter(
|
||||||
|
name="page_id",
|
||||||
|
location=OpenApiParameter.PATH,
|
||||||
|
type=OpenApiTypes.INT,
|
||||||
|
description="The page the workflow actions belong to",
|
||||||
|
),
|
||||||
|
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
|
||||||
|
],
|
||||||
|
tags=["Builder workflow_actions"],
|
||||||
|
operation_id="order_builder_workflow_actions",
|
||||||
|
description="Apply a new order to the workflow actions of a page",
|
||||||
|
request=OrderWorkflowActionsSerializer,
|
||||||
|
responses={
|
||||||
|
204: None,
|
||||||
|
400: get_error_schema(
|
||||||
|
[
|
||||||
|
"ERROR_USER_NOT_IN_GROUP",
|
||||||
|
"ERROR_REQUEST_BODY_VALIDATION",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
404: get_error_schema(
|
||||||
|
[
|
||||||
|
"ERROR_PAGE_DOES_NOT_EXIST",
|
||||||
|
"ERROR_WORKFLOW_ACTION_DOES_NOT_EXIST",
|
||||||
|
"ERROR_WORKFLOW_ACTION_NOT_IN_ELEMENT",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@transaction.atomic
|
||||||
|
@map_exceptions(
|
||||||
|
{
|
||||||
|
PageDoesNotExist: ERROR_PAGE_DOES_NOT_EXIST,
|
||||||
|
WorkflowActionDoesNotExist: ERROR_WORKFLOW_ACTION_DOES_NOT_EXIST,
|
||||||
|
WorkflowActionNotInElement: ERROR_WORKFLOW_ACTION_NOT_IN_ELEMENT,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@validate_body(OrderWorkflowActionsSerializer)
|
||||||
|
def post(self, request, data: Dict, page_id: int):
|
||||||
|
page = PageHandler().get_page(page_id)
|
||||||
|
element_id = data.get("element_id", None)
|
||||||
|
element = (
|
||||||
|
ElementHandler().get_element(element_id) if element_id is not None else None
|
||||||
|
)
|
||||||
|
|
||||||
|
BuilderWorkflowActionService().order_workflow_actions(
|
||||||
|
request.user, page, data["workflow_action_ids"], element=element
|
||||||
|
)
|
||||||
|
|
||||||
|
return Response(status=204)
|
||||||
|
|
|
@ -136,6 +136,7 @@ class BuilderConfig(AppConfig):
|
||||||
CreateBuilderWorkflowActionOperationType,
|
CreateBuilderWorkflowActionOperationType,
|
||||||
DeleteBuilderWorkflowActionOperationType,
|
DeleteBuilderWorkflowActionOperationType,
|
||||||
ListBuilderWorkflowActionsPageOperationType,
|
ListBuilderWorkflowActionsPageOperationType,
|
||||||
|
OrderBuilderWorkflowActionOperationType,
|
||||||
ReadBuilderWorkflowActionOperationType,
|
ReadBuilderWorkflowActionOperationType,
|
||||||
UpdateBuilderWorkflowActionOperationType,
|
UpdateBuilderWorkflowActionOperationType,
|
||||||
)
|
)
|
||||||
|
@ -145,6 +146,7 @@ class BuilderConfig(AppConfig):
|
||||||
operation_type_registry.register(DeleteBuilderWorkflowActionOperationType())
|
operation_type_registry.register(DeleteBuilderWorkflowActionOperationType())
|
||||||
operation_type_registry.register(UpdateBuilderWorkflowActionOperationType())
|
operation_type_registry.register(UpdateBuilderWorkflowActionOperationType())
|
||||||
operation_type_registry.register(ReadBuilderWorkflowActionOperationType())
|
operation_type_registry.register(ReadBuilderWorkflowActionOperationType())
|
||||||
|
operation_type_registry.register(OrderBuilderWorkflowActionOperationType())
|
||||||
|
|
||||||
from baserow.core.registries import permission_manager_type_registry
|
from baserow.core.registries import permission_manager_type_registry
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 3.2.21 on 2023-10-31 10:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("builder", "0029_inputtextelement_label"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="builderworkflowaction",
|
||||||
|
name="order",
|
||||||
|
field=models.PositiveIntegerField(default=0),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,10 @@
|
||||||
|
class WorkflowActionNotInElement(Exception):
|
||||||
|
"""Raised when trying to get a workflow action that does not belong to an element"""
|
||||||
|
|
||||||
|
def __init__(self, workflow_action_id=None, *args, **kwargs):
|
||||||
|
self.workflow_action_id = workflow_action_id
|
||||||
|
super().__init__(
|
||||||
|
f"The workflow action {workflow_action_id} does not belong to the element.",
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
)
|
|
@ -1,16 +1,23 @@
|
||||||
from typing import Dict, Iterable, Optional
|
from typing import Dict, Iterable, List, Optional
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from django.core.files.storage import Storage
|
from django.core.files.storage import Storage
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
|
from baserow.contrib.builder.elements.handler import ElementHandler
|
||||||
|
from baserow.contrib.builder.elements.models import Element
|
||||||
from baserow.contrib.builder.pages.models import Page
|
from baserow.contrib.builder.pages.models import Page
|
||||||
|
from baserow.contrib.builder.workflow_actions.exceptions import (
|
||||||
|
WorkflowActionNotInElement,
|
||||||
|
)
|
||||||
from baserow.contrib.builder.workflow_actions.models import BuilderWorkflowAction
|
from baserow.contrib.builder.workflow_actions.models import BuilderWorkflowAction
|
||||||
from baserow.contrib.builder.workflow_actions.registries import (
|
from baserow.contrib.builder.workflow_actions.registries import (
|
||||||
builder_workflow_action_type_registry,
|
builder_workflow_action_type_registry,
|
||||||
)
|
)
|
||||||
|
from baserow.core.exceptions import IdDoesNotExist
|
||||||
from baserow.core.workflow_actions.handler import WorkflowActionHandler
|
from baserow.core.workflow_actions.handler import WorkflowActionHandler
|
||||||
from baserow.core.workflow_actions.models import WorkflowAction
|
from baserow.core.workflow_actions.models import WorkflowAction
|
||||||
|
from baserow.core.workflow_actions.registries import WorkflowActionType
|
||||||
|
|
||||||
|
|
||||||
class BuilderWorkflowActionHandler(WorkflowActionHandler):
|
class BuilderWorkflowActionHandler(WorkflowActionHandler):
|
||||||
|
@ -44,6 +51,7 @@ class BuilderWorkflowActionHandler(WorkflowActionHandler):
|
||||||
kwargs["page_id"] = workflow_action.page_id
|
kwargs["page_id"] = workflow_action.page_id
|
||||||
kwargs["element_id"] = workflow_action.element_id
|
kwargs["element_id"] = workflow_action.element_id
|
||||||
kwargs["event"] = workflow_action.event
|
kwargs["event"] = workflow_action.event
|
||||||
|
kwargs["order"] = workflow_action.order
|
||||||
|
|
||||||
return super().update_workflow_action(workflow_action, **kwargs)
|
return super().update_workflow_action(workflow_action, **kwargs)
|
||||||
|
|
||||||
|
@ -74,3 +82,45 @@ class BuilderWorkflowActionHandler(WorkflowActionHandler):
|
||||||
return workflow_action_type.import_serialized(
|
return workflow_action_type.import_serialized(
|
||||||
page, serialized_workflow_action, id_mapping
|
page, serialized_workflow_action, id_mapping
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def order_workflow_actions(
|
||||||
|
self, page: Page, order: List[int], base_qs=None, element: Element = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Assigns a new order to the domains in a builder application.
|
||||||
|
You can provide a base_qs for pre-filter the domains affected by this change.
|
||||||
|
|
||||||
|
:param page: The page the workflow actions belong to
|
||||||
|
:param order: The new order of the workflow actions
|
||||||
|
:param base_qs: A QS that can have filters already applied
|
||||||
|
:param element: The element the workflow action belongs to
|
||||||
|
:raises WorkflowActionNotInElement: If the workflow action is not part of the
|
||||||
|
provided element
|
||||||
|
:return: The new order of the domains
|
||||||
|
"""
|
||||||
|
|
||||||
|
if base_qs is None:
|
||||||
|
base_qs = BuilderWorkflowAction.objects.filter(page=page, element=element)
|
||||||
|
|
||||||
|
try:
|
||||||
|
full_order = BuilderWorkflowAction.order_objects(base_qs, order)
|
||||||
|
except IdDoesNotExist as error:
|
||||||
|
raise WorkflowActionNotInElement(error.not_existing_id)
|
||||||
|
|
||||||
|
return full_order
|
||||||
|
|
||||||
|
def create_workflow_action(
|
||||||
|
self, workflow_action_type: WorkflowActionType, **kwargs
|
||||||
|
) -> BuilderWorkflowAction:
|
||||||
|
if "order" not in kwargs:
|
||||||
|
if "element_id" in kwargs:
|
||||||
|
element = ElementHandler().get_element(element_id=kwargs["element_id"])
|
||||||
|
kwargs["order"] = BuilderWorkflowAction.get_last_order_element_scope(
|
||||||
|
element
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
kwargs["order"] = BuilderWorkflowAction.get_last_order_page_scope(
|
||||||
|
kwargs["page"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().create_workflow_action(workflow_action_type, **kwargs).specific
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django.db import models
|
||||||
from baserow.contrib.builder.elements.models import Element
|
from baserow.contrib.builder.elements.models import Element
|
||||||
from baserow.contrib.builder.pages.models import Page
|
from baserow.contrib.builder.pages.models import Page
|
||||||
from baserow.core.formula.field import FormulaField
|
from baserow.core.formula.field import FormulaField
|
||||||
|
from baserow.core.mixins import OrderableMixin
|
||||||
from baserow.core.registry import ModelRegistryMixin
|
from baserow.core.registry import ModelRegistryMixin
|
||||||
from baserow.core.workflow_actions.models import WorkflowAction
|
from baserow.core.workflow_actions.models import WorkflowAction
|
||||||
|
|
||||||
|
@ -12,7 +13,11 @@ class EventTypes(models.TextChoices):
|
||||||
CLICK = "click"
|
CLICK = "click"
|
||||||
|
|
||||||
|
|
||||||
class BuilderWorkflowAction(WorkflowAction):
|
class BuilderWorkflowAction(
|
||||||
|
WorkflowAction,
|
||||||
|
OrderableMixin,
|
||||||
|
):
|
||||||
|
order = models.PositiveIntegerField()
|
||||||
content_type = models.ForeignKey(
|
content_type = models.ForeignKey(
|
||||||
ContentType,
|
ContentType,
|
||||||
verbose_name="content type",
|
verbose_name="content type",
|
||||||
|
@ -40,6 +45,16 @@ class BuilderWorkflowAction(WorkflowAction):
|
||||||
def get_parent(self):
|
def get_parent(self):
|
||||||
return self.page
|
return self.page
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_last_order_element_scope(cls, element: Element):
|
||||||
|
queryset = BuilderWorkflowAction.objects.filter(element=element)
|
||||||
|
return cls.get_highest_order_of_queryset(queryset) + 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_last_order_page_scope(cls, page: Page):
|
||||||
|
queryset = BuilderWorkflowAction.objects.filter(page=page, element=None)
|
||||||
|
return cls.get_highest_order_of_queryset(queryset) + 1
|
||||||
|
|
||||||
|
|
||||||
class NotificationWorkflowAction(BuilderWorkflowAction):
|
class NotificationWorkflowAction(BuilderWorkflowAction):
|
||||||
title = FormulaField(default="")
|
title = FormulaField(default="")
|
||||||
|
|
|
@ -13,6 +13,10 @@ class CreateBuilderWorkflowActionOperationType(BuilderPageOperationType):
|
||||||
type = "builder.page.create_workflow_action"
|
type = "builder.page.create_workflow_action"
|
||||||
|
|
||||||
|
|
||||||
|
class OrderBuilderWorkflowActionOperationType(BuilderPageOperationType):
|
||||||
|
type = "builder.page.workflow_action.order"
|
||||||
|
|
||||||
|
|
||||||
class BuilderWorkflowActionOperationType(OperationType, ABC):
|
class BuilderWorkflowActionOperationType(OperationType, ABC):
|
||||||
context_scope_name = "builder_workflow_action"
|
context_scope_name = "builder_workflow_action"
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from baserow.core.workflow_actions.registries import WorkflowActionType
|
||||||
|
|
||||||
|
|
||||||
class BuilderWorkflowActionType(WorkflowActionType, PublicCustomFieldsInstanceMixin):
|
class BuilderWorkflowActionType(WorkflowActionType, PublicCustomFieldsInstanceMixin):
|
||||||
allowed_fields = ["page", "page_id", "element", "element_id", "event"]
|
allowed_fields = ["order", "page", "page_id", "element", "element_id", "event"]
|
||||||
|
|
||||||
parent_property_name = "page"
|
parent_property_name = "page"
|
||||||
id_mapping_name = "builder_workflow_actions"
|
id_mapping_name = "builder_workflow_actions"
|
||||||
|
|
|
@ -2,6 +2,7 @@ from typing import List
|
||||||
|
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
|
||||||
|
from baserow.contrib.builder.elements.models import Element
|
||||||
from baserow.contrib.builder.pages.models import Page
|
from baserow.contrib.builder.pages.models import Page
|
||||||
from baserow.contrib.builder.workflow_actions.handler import (
|
from baserow.contrib.builder.workflow_actions.handler import (
|
||||||
BuilderWorkflowActionHandler,
|
BuilderWorkflowActionHandler,
|
||||||
|
@ -14,6 +15,7 @@ from baserow.contrib.builder.workflow_actions.operations import (
|
||||||
CreateBuilderWorkflowActionOperationType,
|
CreateBuilderWorkflowActionOperationType,
|
||||||
DeleteBuilderWorkflowActionOperationType,
|
DeleteBuilderWorkflowActionOperationType,
|
||||||
ListBuilderWorkflowActionsPageOperationType,
|
ListBuilderWorkflowActionsPageOperationType,
|
||||||
|
OrderBuilderWorkflowActionOperationType,
|
||||||
ReadBuilderWorkflowActionOperationType,
|
ReadBuilderWorkflowActionOperationType,
|
||||||
UpdateBuilderWorkflowActionOperationType,
|
UpdateBuilderWorkflowActionOperationType,
|
||||||
)
|
)
|
||||||
|
@ -21,6 +23,7 @@ from baserow.contrib.builder.workflow_actions.signals import (
|
||||||
workflow_action_created,
|
workflow_action_created,
|
||||||
workflow_action_deleted,
|
workflow_action_deleted,
|
||||||
workflow_action_updated,
|
workflow_action_updated,
|
||||||
|
workflow_actions_reordered,
|
||||||
)
|
)
|
||||||
from baserow.contrib.builder.workflow_actions.workflow_action_types import (
|
from baserow.contrib.builder.workflow_actions.workflow_action_types import (
|
||||||
BuilderWorkflowActionType,
|
BuilderWorkflowActionType,
|
||||||
|
@ -172,3 +175,46 @@ class BuilderWorkflowActionService:
|
||||||
workflow_action_deleted.send(
|
workflow_action_deleted.send(
|
||||||
self, workflow_action_id=workflow_action.id, page=page, user=user
|
self, workflow_action_id=workflow_action.id, page=page, user=user
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def order_workflow_actions(
|
||||||
|
self,
|
||||||
|
user: AbstractUser,
|
||||||
|
page: Page,
|
||||||
|
order: List[int],
|
||||||
|
element: Element = None,
|
||||||
|
) -> List[int]:
|
||||||
|
"""
|
||||||
|
Assigns a new order to the workflow actions in a builder application.
|
||||||
|
|
||||||
|
:param user: The user trying to order the domains
|
||||||
|
:param page: The page that the workflow actions belong to
|
||||||
|
:param order: The new order of the workflow actions
|
||||||
|
:param element: The element the page belongs to
|
||||||
|
:return: The new order of the workflow actions
|
||||||
|
"""
|
||||||
|
|
||||||
|
CoreHandler().check_permissions(
|
||||||
|
user,
|
||||||
|
OrderBuilderWorkflowActionOperationType.type,
|
||||||
|
workspace=page.builder.workspace,
|
||||||
|
context=page,
|
||||||
|
)
|
||||||
|
|
||||||
|
all_workflow_actions = BuilderWorkflowAction.objects.filter(
|
||||||
|
page=page, element=element
|
||||||
|
)
|
||||||
|
|
||||||
|
user_workflow_actions = CoreHandler().filter_queryset(
|
||||||
|
user,
|
||||||
|
OrderBuilderWorkflowActionOperationType.type,
|
||||||
|
all_workflow_actions,
|
||||||
|
workspace=page.builder.workspace,
|
||||||
|
)
|
||||||
|
|
||||||
|
full_order = self.handler.order_workflow_actions(
|
||||||
|
page, order, base_qs=user_workflow_actions, element=element
|
||||||
|
)
|
||||||
|
|
||||||
|
workflow_actions_reordered.send(self, order=full_order, user=user)
|
||||||
|
|
||||||
|
return full_order
|
||||||
|
|
|
@ -3,3 +3,4 @@ from django.dispatch import Signal
|
||||||
workflow_action_created = Signal()
|
workflow_action_created = Signal()
|
||||||
workflow_action_deleted = Signal()
|
workflow_action_deleted = Signal()
|
||||||
workflow_action_updated = Signal()
|
workflow_action_updated = Signal()
|
||||||
|
workflow_actions_reordered = Signal()
|
||||||
|
|
|
@ -3,7 +3,6 @@ from django.db import models
|
||||||
from baserow.core.mixins import (
|
from baserow.core.mixins import (
|
||||||
CreatedAndUpdatedOnMixin,
|
CreatedAndUpdatedOnMixin,
|
||||||
HierarchicalModelMixin,
|
HierarchicalModelMixin,
|
||||||
OrderableMixin,
|
|
||||||
PolymorphicContentTypeMixin,
|
PolymorphicContentTypeMixin,
|
||||||
WithRegistry,
|
WithRegistry,
|
||||||
)
|
)
|
||||||
|
@ -14,7 +13,6 @@ class WorkflowAction(
|
||||||
PolymorphicContentTypeMixin,
|
PolymorphicContentTypeMixin,
|
||||||
CreatedAndUpdatedOnMixin,
|
CreatedAndUpdatedOnMixin,
|
||||||
HierarchicalModelMixin,
|
HierarchicalModelMixin,
|
||||||
OrderableMixin,
|
|
||||||
models.Model,
|
models.Model,
|
||||||
WithRegistry,
|
WithRegistry,
|
||||||
):
|
):
|
||||||
|
|
|
@ -6,6 +6,7 @@ from baserow.core.workflow_actions.models import WorkflowAction
|
||||||
class WorkflowActionDict(TypedDict):
|
class WorkflowActionDict(TypedDict):
|
||||||
id: int
|
id: int
|
||||||
type: str
|
type: str
|
||||||
|
order: int
|
||||||
|
|
||||||
|
|
||||||
WorkflowActionDictSubClass = TypeVar(
|
WorkflowActionDictSubClass = TypeVar(
|
||||||
|
|
|
@ -6,4 +6,10 @@ class WorkflowActionFixture:
|
||||||
return self.create_workflow_action(NotificationWorkflowAction, **kwargs)
|
return self.create_workflow_action(NotificationWorkflowAction, **kwargs)
|
||||||
|
|
||||||
def create_workflow_action(self, model_class, **kwargs):
|
def create_workflow_action(self, model_class, **kwargs):
|
||||||
|
if "order" not in "kwargs":
|
||||||
|
kwargs["order"] = 0
|
||||||
|
|
||||||
|
if "page" not in kwargs and "element" in kwargs:
|
||||||
|
kwargs["page"] = kwargs["element"].page
|
||||||
|
|
||||||
return model_class.objects.create(**kwargs)
|
return model_class.objects.create(**kwargs)
|
||||||
|
|
|
@ -203,3 +203,105 @@ def test_public_workflow_actions_view(
|
||||||
|
|
||||||
[workflow_action_in_response] = response.json()
|
[workflow_action_in_response] = response.json()
|
||||||
assert "test" in workflow_action_in_response
|
assert "test" in workflow_action_in_response
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
page = data_fixture.create_builder_page(user=user)
|
||||||
|
element = data_fixture.create_builder_button_element(page=page)
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
page=page, element=element, order=1
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
page=page, element=element, order=2
|
||||||
|
)
|
||||||
|
|
||||||
|
order = [workflow_action_two.id, workflow_action_one.id]
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"api:builder:workflow_action:order",
|
||||||
|
kwargs={"page_id": page.id},
|
||||||
|
)
|
||||||
|
response = api_client.post(
|
||||||
|
url,
|
||||||
|
{"workflow_action_ids": order, "element_id": element.id},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_204_NO_CONTENT
|
||||||
|
|
||||||
|
workflow_action_one.refresh_from_db()
|
||||||
|
workflow_action_two.refresh_from_db()
|
||||||
|
|
||||||
|
assert workflow_action_one.order > workflow_action_two.order
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions_page_does_not_exist(api_client, data_fixture):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"api:builder:workflow_action:order",
|
||||||
|
kwargs={"page_id": 99999},
|
||||||
|
)
|
||||||
|
response = api_client.post(
|
||||||
|
url,
|
||||||
|
{"workflow_action_ids": []},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_404_NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions_workflow_action_does_not_exist(
|
||||||
|
api_client, data_fixture
|
||||||
|
):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
page = data_fixture.create_builder_page(user=user)
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"api:builder:workflow_action:order",
|
||||||
|
kwargs={"page_id": page.id},
|
||||||
|
)
|
||||||
|
response = api_client.post(
|
||||||
|
url,
|
||||||
|
{"workflow_action_ids": [9999]},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_404_NOT_FOUND
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions_workflow_action_not_in_element(
|
||||||
|
api_client, data_fixture
|
||||||
|
):
|
||||||
|
user, token = data_fixture.create_user_and_token()
|
||||||
|
page = data_fixture.create_builder_page(user=user)
|
||||||
|
element = data_fixture.create_builder_button_element(page=page)
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
page=page, element=element, order=1
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
page=page, order=2
|
||||||
|
)
|
||||||
|
|
||||||
|
order = [workflow_action_two.id, workflow_action_one.id]
|
||||||
|
|
||||||
|
url = reverse(
|
||||||
|
"api:builder:workflow_action:order",
|
||||||
|
kwargs={"page_id": page.id},
|
||||||
|
)
|
||||||
|
response = api_client.post(
|
||||||
|
url,
|
||||||
|
{"workflow_action_ids": order, "element_id": element.id},
|
||||||
|
format="json",
|
||||||
|
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == HTTP_404_NOT_FOUND
|
||||||
|
|
|
@ -95,6 +95,7 @@ def test_builder_application_export(data_fixture):
|
||||||
"workflow_actions": [
|
"workflow_actions": [
|
||||||
{
|
{
|
||||||
"id": workflow_action_1.id,
|
"id": workflow_action_1.id,
|
||||||
|
"order": 0,
|
||||||
"type": "notification",
|
"type": "notification",
|
||||||
"element_id": element1.id,
|
"element_id": element1.id,
|
||||||
"event": EventTypes.CLICK.value,
|
"event": EventTypes.CLICK.value,
|
||||||
|
@ -329,6 +330,7 @@ IMPORT_REFERENCE = {
|
||||||
"workflow_actions": [
|
"workflow_actions": [
|
||||||
{
|
{
|
||||||
"id": 123,
|
"id": 123,
|
||||||
|
"order": 1,
|
||||||
"page_id": 999,
|
"page_id": 999,
|
||||||
"element_id": 998,
|
"element_id": 998,
|
||||||
"type": "notification",
|
"type": "notification",
|
||||||
|
|
|
@ -41,7 +41,12 @@ def test_import_workflow_action(data_fixture, workflow_action_type: WorkflowActi
|
||||||
|
|
||||||
page_after_import = data_fixture.create_builder_page()
|
page_after_import = data_fixture.create_builder_page()
|
||||||
|
|
||||||
serialized = {"id": 9999, "type": workflow_action_type.type, "page_id": page.id}
|
serialized = {
|
||||||
|
"id": 9999,
|
||||||
|
"type": workflow_action_type.type,
|
||||||
|
"page_id": page.id,
|
||||||
|
"order": 0,
|
||||||
|
}
|
||||||
serialized.update(workflow_action_type.get_sample_params())
|
serialized.update(workflow_action_type.get_sample_params())
|
||||||
|
|
||||||
id_mapping = {"builder_pages": {page.id: page_after_import.id}}
|
id_mapping = {"builder_pages": {page.id: page_after_import.id}}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from baserow.contrib.builder.workflow_actions.exceptions import (
|
||||||
|
WorkflowActionNotInElement,
|
||||||
|
)
|
||||||
from baserow.contrib.builder.workflow_actions.handler import (
|
from baserow.contrib.builder.workflow_actions.handler import (
|
||||||
BuilderWorkflowActionHandler,
|
BuilderWorkflowActionHandler,
|
||||||
)
|
)
|
||||||
|
@ -122,3 +125,65 @@ def test_get_workflow_actions(data_fixture):
|
||||||
|
|
||||||
assert workflow_action_one_fetched.id == workflow_action_one.id
|
assert workflow_action_one_fetched.id == workflow_action_one.id
|
||||||
assert workflow_action_two_fetched.id == workflow_action_two.id
|
assert workflow_action_two_fetched.id == workflow_action_two.id
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions(data_fixture):
|
||||||
|
element = data_fixture.create_builder_button_element()
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
page=element.page, element=element, order=1
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
page=element.page, element=element, order=2
|
||||||
|
)
|
||||||
|
|
||||||
|
assert BuilderWorkflowActionHandler().order_workflow_actions(
|
||||||
|
element.page,
|
||||||
|
[workflow_action_two.id, workflow_action_one.id],
|
||||||
|
element=element,
|
||||||
|
) == [
|
||||||
|
workflow_action_two.id,
|
||||||
|
workflow_action_one.id,
|
||||||
|
]
|
||||||
|
|
||||||
|
workflow_action_one.refresh_from_db()
|
||||||
|
workflow_action_two.refresh_from_db()
|
||||||
|
|
||||||
|
assert workflow_action_one.order == 2
|
||||||
|
assert workflow_action_two.order == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_action_not_in_element(data_fixture):
|
||||||
|
element = data_fixture.create_builder_button_element()
|
||||||
|
element_unrelated = data_fixture.create_builder_button_element()
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
page=element.page, element=element, order=1
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
page=element_unrelated.page, order=2
|
||||||
|
)
|
||||||
|
|
||||||
|
base_qs = BuilderWorkflowAction.objects.filter(id=workflow_action_two.id)
|
||||||
|
|
||||||
|
with pytest.raises(WorkflowActionNotInElement):
|
||||||
|
BuilderWorkflowActionHandler().order_workflow_actions(
|
||||||
|
element.page,
|
||||||
|
[workflow_action_two.id, workflow_action_one.id],
|
||||||
|
element=element,
|
||||||
|
base_qs=base_qs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions_different_scopes(data_fixture):
|
||||||
|
page = data_fixture.create_builder_page()
|
||||||
|
element = data_fixture.create_builder_button_element(page=page)
|
||||||
|
page_workflow_action = BuilderWorkflowActionHandler().create_workflow_action(
|
||||||
|
NotificationWorkflowActionType(), page=page
|
||||||
|
)
|
||||||
|
element_workflow_action = BuilderWorkflowActionHandler().create_workflow_action(
|
||||||
|
NotificationWorkflowActionType(), page=page, element_id=element.id
|
||||||
|
)
|
||||||
|
|
||||||
|
assert page_workflow_action.order == element_workflow_action.order
|
||||||
|
|
|
@ -184,3 +184,47 @@ def test_get_workflow_actions_no_permissions(data_fixture):
|
||||||
|
|
||||||
with pytest.raises(UserNotInWorkspace):
|
with pytest.raises(UserNotInWorkspace):
|
||||||
BuilderWorkflowActionService().get_workflow_actions(user, page)
|
BuilderWorkflowActionService().get_workflow_actions(user, page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions(data_fixture):
|
||||||
|
user = data_fixture.create_user()
|
||||||
|
element = data_fixture.create_builder_button_element(user=user)
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
element=element, order=0
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
element=element, order=1
|
||||||
|
)
|
||||||
|
|
||||||
|
BuilderWorkflowActionService().order_workflow_actions(
|
||||||
|
user,
|
||||||
|
element.page,
|
||||||
|
[workflow_action_two.id, workflow_action_one.id],
|
||||||
|
element=element,
|
||||||
|
)
|
||||||
|
|
||||||
|
workflow_action_one.refresh_from_db()
|
||||||
|
workflow_action_two.refresh_from_db()
|
||||||
|
|
||||||
|
assert workflow_action_one.order > workflow_action_two.order
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_order_workflow_actions_user_not_in_workspace(data_fixture):
|
||||||
|
user = data_fixture.create_user()
|
||||||
|
element = data_fixture.create_builder_button_element()
|
||||||
|
workflow_action_one = data_fixture.create_notification_workflow_action(
|
||||||
|
element=element, order=0
|
||||||
|
)
|
||||||
|
workflow_action_two = data_fixture.create_notification_workflow_action(
|
||||||
|
element=element, order=1
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(UserNotInWorkspace):
|
||||||
|
BuilderWorkflowActionService().order_workflow_actions(
|
||||||
|
user,
|
||||||
|
element.page,
|
||||||
|
[workflow_action_two.id, workflow_action_one.id],
|
||||||
|
element=element,
|
||||||
|
)
|
||||||
|
|
|
@ -52,6 +52,7 @@ from baserow.contrib.builder.workflow_actions.operations import (
|
||||||
CreateBuilderWorkflowActionOperationType,
|
CreateBuilderWorkflowActionOperationType,
|
||||||
DeleteBuilderWorkflowActionOperationType,
|
DeleteBuilderWorkflowActionOperationType,
|
||||||
ListBuilderWorkflowActionsPageOperationType,
|
ListBuilderWorkflowActionsPageOperationType,
|
||||||
|
OrderBuilderWorkflowActionOperationType,
|
||||||
ReadBuilderWorkflowActionOperationType,
|
ReadBuilderWorkflowActionOperationType,
|
||||||
UpdateBuilderWorkflowActionOperationType,
|
UpdateBuilderWorkflowActionOperationType,
|
||||||
)
|
)
|
||||||
|
@ -323,6 +324,7 @@ default_roles[EDITOR_ROLE_UID].extend(
|
||||||
ListTeamSubjectsOperationType,
|
ListTeamSubjectsOperationType,
|
||||||
ReadTeamSubjectOperationType,
|
ReadTeamSubjectOperationType,
|
||||||
UpdateBuilderWorkflowActionOperationType,
|
UpdateBuilderWorkflowActionOperationType,
|
||||||
|
OrderBuilderWorkflowActionOperationType,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
default_roles[BUILDER_ROLE_UID].extend(
|
default_roles[BUILDER_ROLE_UID].extend(
|
||||||
|
|
|
@ -19,15 +19,22 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<WorkflowAction
|
<div>
|
||||||
v-for="workflowAction in workflowActions"
|
<WorkflowAction
|
||||||
:key="workflowAction.id"
|
v-for="(workflowAction, index) in workflowActions"
|
||||||
class="margin-top-2 event__workflow-action"
|
:key="workflowAction.id"
|
||||||
:available-workflow-action-types="availableWorkflowActionTypes"
|
v-sortable="{
|
||||||
:workflow-action="workflowAction"
|
id: workflowAction.id,
|
||||||
@delete="deleteWorkflowAction(workflowAction)"
|
handle: '[data-sortable-handle]',
|
||||||
@update="updateWorkflowAction(workflowAction, $event)"
|
update: orderWorkflowActions,
|
||||||
/>
|
}"
|
||||||
|
class="event__workflow-action"
|
||||||
|
:class="{ 'event__workflow-action--first': index === 0 }"
|
||||||
|
:available-workflow-action-types="availableWorkflowActionTypes"
|
||||||
|
:workflow-action="workflowAction"
|
||||||
|
@delete="deleteWorkflowAction(workflowAction)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Button
|
<Button
|
||||||
size="tiny"
|
size="tiny"
|
||||||
type="link"
|
type="link"
|
||||||
|
@ -82,6 +89,7 @@ export default {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
actionCreateWorkflowAction: 'workflowAction/create',
|
actionCreateWorkflowAction: 'workflowAction/create',
|
||||||
actionDeleteWorkflowAction: 'workflowAction/delete',
|
actionDeleteWorkflowAction: 'workflowAction/delete',
|
||||||
|
actionOrderWorkflowActions: 'workflowAction/order',
|
||||||
}),
|
}),
|
||||||
getIcon(expanded) {
|
getIcon(expanded) {
|
||||||
return expanded ? 'iconoir-nav-arrow-down' : 'iconoir-nav-arrow-right'
|
return expanded ? 'iconoir-nav-arrow-down' : 'iconoir-nav-arrow-right'
|
||||||
|
@ -112,6 +120,17 @@ export default {
|
||||||
notifyIf(error)
|
notifyIf(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async orderWorkflowActions(order) {
|
||||||
|
try {
|
||||||
|
await this.actionOrderWorkflowActions({
|
||||||
|
page: this.page,
|
||||||
|
element: this.element,
|
||||||
|
order,
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
notifyIf(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -21,5 +21,17 @@ export default (client) => {
|
||||||
values
|
values
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
order(pageId, order, elementId = null) {
|
||||||
|
const payload = { workflow_action_ids: order }
|
||||||
|
|
||||||
|
if (elementId) {
|
||||||
|
payload.element_id = elementId
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.post(
|
||||||
|
`builder/page/${pageId}/workflow_actions/order/`,
|
||||||
|
payload
|
||||||
|
)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,12 @@ const mutations = {
|
||||||
workflowAction.id === workflowActionToSet.id ? values : workflowAction
|
workflowAction.id === workflowActionToSet.id ? values : workflowAction
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
ORDER_ITEMS(state, { page, order }) {
|
||||||
|
page.workflowActions.forEach((workflowAction) => {
|
||||||
|
const index = order.findIndex((value) => value === workflowAction.id)
|
||||||
|
workflowAction.order = index === -1 ? 0 : index + 1
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
|
@ -51,6 +57,9 @@ const actions = {
|
||||||
forceSet({ commit }, { page, workflowAction, values }) {
|
forceSet({ commit }, { page, workflowAction, values }) {
|
||||||
commit('SET_ITEM', { page, workflowAction, values })
|
commit('SET_ITEM', { page, workflowAction, values })
|
||||||
},
|
},
|
||||||
|
forceOrder({ commit }, { page, order }) {
|
||||||
|
commit('ORDER_ITEMS', { page, order })
|
||||||
|
},
|
||||||
async create(
|
async create(
|
||||||
{ dispatch },
|
{ dispatch },
|
||||||
{ page, workflowActionType, eventType, configuration = null }
|
{ page, workflowActionType, eventType, configuration = null }
|
||||||
|
@ -87,34 +96,17 @@ const actions = {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async update({ dispatch }, { page, workflowAction, values }) {
|
|
||||||
const oldValues = {}
|
|
||||||
const newValues = {}
|
|
||||||
Object.keys(values).forEach((name) => {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(workflowAction, name)) {
|
|
||||||
oldValues[name] = workflowAction[name]
|
|
||||||
newValues[name] = values[name]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await dispatch('forceUpdate', { page, workflowAction, values: newValues })
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { data } = await WorkflowActionService(this.$client).update(
|
|
||||||
workflowAction.id,
|
|
||||||
values
|
|
||||||
)
|
|
||||||
await dispatch('forceSet', { page, workflowAction, values: data })
|
|
||||||
} catch (error) {
|
|
||||||
await dispatch('forceUpdate', { page, workflowAction, values: oldValues })
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async updateDebounced({ dispatch }, { page, workflowAction, values }) {
|
async updateDebounced({ dispatch }, { page, workflowAction, values }) {
|
||||||
|
// These values should not be updated via a regular update request
|
||||||
|
const excludeValues = ['order']
|
||||||
|
|
||||||
const oldValues = {}
|
const oldValues = {}
|
||||||
const newValues = {}
|
const newValues = {}
|
||||||
Object.keys(values).forEach((name) => {
|
Object.keys(values).forEach((name) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(workflowAction, name)) {
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(workflowAction, name) &&
|
||||||
|
!excludeValues.includes(name)
|
||||||
|
) {
|
||||||
oldValues[name] = workflowAction[name]
|
oldValues[name] = workflowAction[name]
|
||||||
newValues[name] = values[name]
|
newValues[name] = values[name]
|
||||||
}
|
}
|
||||||
|
@ -129,7 +121,12 @@ const actions = {
|
||||||
workflowAction.id,
|
workflowAction.id,
|
||||||
values
|
values
|
||||||
)
|
)
|
||||||
await dispatch('forceSet', {
|
|
||||||
|
excludeValues.forEach((name) => {
|
||||||
|
delete data[name]
|
||||||
|
})
|
||||||
|
|
||||||
|
await dispatch('forceUpdate', {
|
||||||
page,
|
page,
|
||||||
workflowAction,
|
workflowAction,
|
||||||
values: data,
|
values: data,
|
||||||
|
@ -161,13 +158,37 @@ const actions = {
|
||||||
updateContext.promiseResolve = resolve
|
updateContext.promiseResolve = resolve
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
async order({ commit, getters }, { page, order, element = null }) {
|
||||||
|
const workflowActions =
|
||||||
|
element !== null
|
||||||
|
? getters.getElementWorkflowActions(page, element.id)
|
||||||
|
: getters.getWorkflowActions(page)
|
||||||
|
|
||||||
|
const oldOrder = workflowActions.map(({ id }) => id)
|
||||||
|
|
||||||
|
commit('ORDER_ITEMS', { page, order })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await WorkflowActionService(this.$client).order(
|
||||||
|
page.id,
|
||||||
|
order,
|
||||||
|
element.id
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
commit('ORDER_ITEMS', { page, order: oldOrder })
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
|
getWorkflowActions: (state) => (page) => {
|
||||||
|
return page.workflowActions.map((w) => w).sort((a, b) => a.order - b.order)
|
||||||
|
},
|
||||||
getElementWorkflowActions: (state) => (page, elementId) => {
|
getElementWorkflowActions: (state) => (page, elementId) => {
|
||||||
return page.workflowActions.filter(
|
return page.workflowActions
|
||||||
(workflowAction) => workflowAction.element_id === elementId
|
.filter((workflowAction) => workflowAction.element_id === elementId)
|
||||||
)
|
.sort((a, b) => a.order - b.order)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,3 +144,4 @@
|
||||||
@import 'anchor';
|
@import 'anchor';
|
||||||
@import 'call_to_action';
|
@import 'call_to_action';
|
||||||
@import 'toast_button';
|
@import 'toast_button';
|
||||||
|
@import 'workflow_action';
|
||||||
|
|
|
@ -22,7 +22,14 @@
|
||||||
color: $color-neutral-900;
|
color: $color-neutral-900;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event__workflow-action:not(:first-child) {
|
.event__workflow-action {
|
||||||
border-top: 1px solid $color-neutral-200;
|
border-top: 1px solid $color-neutral-200;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
|
||||||
|
// :first-child doesn't work here as the draggable directives adds an element
|
||||||
|
&.event__workflow-action--first {
|
||||||
|
border-top: none;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
.workflow-action-selector {
|
.workflow-action-selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-action-selector__options {
|
.workflow-action-selector__options {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
.workflow-action__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-action__header-handle {
|
||||||
|
width: 15px;
|
||||||
|
height: 25px;
|
||||||
|
background-image: radial-gradient($color-neutral-200 40%, transparent 40%);
|
||||||
|
background-size: 5px 5px;
|
||||||
|
background-repeat: repeat;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: radial-gradient($color-neutral-500 40%, transparent 40%);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<WorkflowActionSelector
|
<div class="workflow-action__header">
|
||||||
:available-workflow-action-types="availableWorkflowActionTypes"
|
<div
|
||||||
:workflow-action="workflowAction"
|
class="workflow-action__header-handle margin-right-1"
|
||||||
@change="updateWorkflowAction({ type: $event })"
|
data-sortable-handle
|
||||||
@delete="$emit('delete')"
|
@mousedown.prevent
|
||||||
/>
|
></div>
|
||||||
|
<WorkflowActionSelector
|
||||||
|
:available-workflow-action-types="availableWorkflowActionTypes"
|
||||||
|
:workflow-action="workflowAction"
|
||||||
|
@change="updateWorkflowAction({ type: $event })"
|
||||||
|
@delete="$emit('delete')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<component
|
<component
|
||||||
:is="workflowActionType.form"
|
:is="workflowActionType.form"
|
||||||
ref="actionForm"
|
ref="actionForm"
|
||||||
|
|
Loading…
Add table
Reference in a new issue