Package: btc-action-set
Features for managing template elements depending on the project role and permission system.Detailed documentation is in the “docs” directory.Quick startAdd “action_set” to your INSTALLED_APPS setting like this:INSTALLED_APPS = (
    ...
    'action_set',
)ActionSet. Prepare base action-set, for example we’ll use built-inActionSetWithPermissionCheck:class BaseActionSet(ActionSetWithPermissionCheck):

    protect_css_class: str = 'protected-element'
    elements_permissions = {
        'action_1': ['change_status'],  # - for search in user permissions
        'action_2': ['edit']
    }

    def get_user_permissions(self) -> list:
        return User.objects.filter(pk=self.request.user.pk).values_list('user_roles__permissions__name', flat=True)

    def check_permissions(self) -> None:
        if not self.request.user.is_superuser:
            return super().check_permissions()Write custom action-set:class MyActionSet(BaseActionSet):

    template = 'action_set/my_action_set.html'

    action_1 = ActionButton(
        'Action 1', css_classes=['js-post_content_btn'],
        html_params={
            'type': 'button',
            'data-url': reverse('app:action_1'),
        }
    )
    action_2 = ActionButton(
        'Action 2', css_classes=['js-load_content_btn'],
        html_params={
            'type': 'button'
        }
    )

    def __init__(self, instance, *args, **kwargs)
        self.instance = instance  # type: MyModel
        super().__init__(*args, **kwargs)
        self.actions['action_2'].html_params.update({'data-url': reverse('app:action_2', self.instance.pk)})Write view, useActionSetMixinViewto construct view:class MyViewWithActionSet(ActionSetMixinView, TemplateView):
    ...
    template_name = 'my_view_template.html'
    action_set_class = MyActionSet
    ...

    def get_action_set_kwargs(self, **kwargs) -> dict:
        return super().get_action_set_kwargs(instance=get_object_or_404(MyModel, pk=self.kwargs['pk']), **kwargs)Prepare template:<!-- action_set/my_action_set.html -->
...
<div class="action-set action-set__my-action-set">
    <div class="action-set__info">
        <i class="material-icons">landscape</i>
        <span>Action Set Test</span>
    </div>
    <div class="action-set__toolbar">
        {% for action in action_set %}
            {{ action }}
        {% endfor %}
    </div>
</div>
...In the template of the view callrendermethod:<!-- my_view_template.html -->
...
<div>{{ action_set.render }}</div>
...ActionSetGroup. Setup group and instantiate action-sets:class CustomActionSetGroup(ActionSetGroup):

    action_set_1 = MyActionSet1
    action_set_2 = MyActionSet2UseActionSetGroupMixinViewto construct custom mixin and view like we did it withActionSetMixinView.
Next setup is same as for theActionSet:class MyView(CustomActionSetGroupMixinView, TemplateView):

    action_set_group_class = CustomActionSetGroup
    template_name = 'my_awesome_page.html'In template:...
<div>
    {{ action_set_group.action_set_1.render }}
</div>
<div>
    {{ action_set_group.action_set_2.render }}
</div>
...10ActionSetMenuMixin. You can building menus by usingActionSetMenuMixin(automatic tabs highlight support).
Note: prepare template as was shown for the simple ActionSet:class MyDetailsMenu(ActionSetMenuMixin, ActionSet or use ActionSetWithPermissionCheck):

    url_attribute_name: str = 'data-url'       # attribute for comparing with current request.path
    activate_class: str = 'active'             # class for active button
    template = 'action_set/details_menu.html'

    menu_tab_1 = ActionButton(
        'TAB1', css_classes=['js-menu-btn'],
        html_params={
            'type': 'button',
            'data-container': '.js-my_content',
            'data-url': reverse_lazy('app:tab_1')
        }
    )
    menu_tab_2 = ActionButton(
        'TAB2', css_classes=['js-menu-btn'],
        html_params={
            'type': 'button',
            'data-container': '.js-my_content',
            'data-url': reverse_lazy('app:tab_2')
        }
    )
    # ... etc.11BreadcrumbsSet. You can use this Action Set for building breadcrumbs. Prepare customBreadcrumbsSet:class MyBreadcrumbsSet(BreadcrumbsSet):

    template = 'my_breadcrumbs.html'
    active_crumb_css_class = 'active-breadcrumb'     # for highlight

    crumb1 = ActionButton(
        'Crumb1',
        html_params={
            'data-url': reverse_lazy('app:tab1'),
            'data-container': '.js-dispatcher-content'
        }
    )
    crumb2 = ActionButton(
        'Crumb2',
        html_params={
            'data-url': reverse_lazy('app:tab2'),
            'data-container': '.js-dispatcher-content'
        }
    )
    # ... etc.12 Prepare yor view for showing breadcrumbs - useBreadcrumbsMixinView:class MyView(BreadcrumbsMixinView, TemplateView):

    breadcrumbs_set_class = MyBreadcrumbsSet
    template_name = 'my_awesome_page.html'

    breadcrumbs_set = ['crumb1', '*crumb2']  # use '*' symbol for showing active breadcrumb.13 Prepare template:<!-- my_breadcrumbs.html -->

{% load dev_tools %}

<div class="action-set action-set__breadcrumbs">
    {% for action in action_set %}
        {{ action|tweak_parameter:'class: mdl-button' }}
        {% if not forloop.last %}
            <span class="breadcrumbs-separator">></span>
        {% endif %}
    {% endfor %}
</div>