Explorar o código

#5406 AI Agents revamp: agents cards actions

Aleksandr Trofimov hai 1 mes
pai
achega
7269a5d37b

+ 32 - 9
inc/js/classes/BxDolGrid.js

@@ -26,6 +26,7 @@ function BxDolGrid (sObject, oOptions) {
     }, oOptions.popup_options);
     this._oQueryAppend = oOptions.query_append;
     this._oConfirmMessages = oOptions.confirm_messages;
+    this.onReload = null;
 
     this.init(oOptions);
 }
@@ -164,6 +165,20 @@ BxDolGrid.prototype._blinkCallback = function (sId) {
     e.removeClass('bx-def-color-bg-active');
 };
 
+BxDolGrid.prototype.actionSingle = function (sAction, sId, oExtra) {
+    oExtra = oExtra || {};
+    var isConfirm = typeof(oExtra.confirm) != 'undefined' ? oExtra.confirm : 0;
+    var iActionResetPaginate = typeof(oExtra.reset_paginate) != 'undefined' ? parseInt(oExtra.reset_paginate) : (String(sAction).indexOf('delete') === -1 ? 0 : 1);
+    var oData = iActionResetPaginate ? {} : this._getActionDataForReload();
+    if (typeof(oExtra.data) == 'object')
+        oData = $.extend({}, oData, oExtra.data);
+    this.actionWithId(sId, sAction, oData, typeof(oExtra.post_data) != 'undefined' ? oExtra.post_data : '', false, isConfirm);
+};
+
+BxDolGrid.prototype.setOnReload = function (fCallback) {
+    this.onReload = typeof(fCallback) == 'function' ? fCallback : null;
+};
+
 BxDolGrid.prototype.actionWithId = function (sId, sAction, oData, sData, isDisableLoading, isConfirm) {
     var sDataAdd = 'ids[]=' + sId;
     if (typeof(sData) == "undefined" || !sData.length) {
@@ -253,12 +268,16 @@ BxDolGrid.prototype.action = function (sAction, oData, sData, isDisableLoading,
 BxDolGrid.prototype.processJson = function (oData, sAction, isDisableLoading) {
     var $this = this;
     var fContinue = function() {
-    	if (oData && undefined != oData.grid) {        
-            $('#' + $this._sIdContainer).html($(oData.grid).find('#' + $this._sIdContainer).html());
-            var sFooter = $(oData.grid).find('.bx-grid-footer').size() ? $(oData.grid).find('.bx-grid-footer').html() : '';
-            $('#' + $this._sIdWrapper).find('.bx-grid-footer').html(sFooter);
-            $('#' + $this._sIdWrapper).find('.bx-grid-header-controls-counter-value').html(oData.total_count_f);
-            $this._onDataReloaded(true);
+    	if (oData && undefined != oData.grid) {
+            if (typeof($this.onReload) == 'function') {
+                $this.onReload(oData);
+            } else {
+                $('#' + $this._sIdContainer).html($(oData.grid).find('#' + $this._sIdContainer).html());
+                var sFooter = $(oData.grid).find('.bx-grid-footer').size() ? $(oData.grid).find('.bx-grid-footer').html() : '';
+                $('#' + $this._sIdWrapper).find('.bx-grid-footer').html(sFooter);
+                $('#' + $this._sIdWrapper).find('.bx-grid-header-controls-counter-value').html(oData.total_count_f);
+                $this._onDataReloaded(true);
+            }
         }
     	if (oData && undefined != oData.blink) {
             if ('object' == typeof(oData.blink)) {
@@ -326,6 +345,8 @@ BxDolGrid.prototype.processJson = function (oData, sAction, isDisableLoading) {
 };
 
 BxDolGrid.prototype.loading = function (bShow) {
+    if (!$('#' + this._sIdContainer).length)
+        return;
     bx_loading(this._sIdContainer, bShow);
 };
 
@@ -385,9 +406,11 @@ BxDolGrid.prototype._bindActionsSingle = function (eElement) {
         var sActionConfirm = $(this).attr('bx_grid_action_confirm');
         var sActionData = $(this).attr('bx_grid_action_data');
         var iActionResetPaginate = parseInt($(this).attr('bx_grid_action_reset_paginate'));
-        var oData = iActionResetPaginate ? {} : $this._getActionDataForReload();
-        oData = $this._checkAppend(this, oData);
-        $this.actionWithId (sActionData, sAction, oData, '', false, sActionConfirm);
+        $this.actionSingle(sAction, sActionData, {
+            confirm: sActionConfirm,
+            reset_paginate: iActionResetPaginate,
+            data: $this._checkAppend(this, {})
+        });
     });
 };
 

+ 30 - 7
studio/js/agents.js

@@ -15,19 +15,42 @@ function BxDolStudioPageAgents(oOptions)
 
     this.sActionUrlCmts = oOptions.sActionUrlCmts == undefined ? this.sActionUrl : oOptions.sActionUrlCmts;
     this.sActionUrlGrid = oOptions.sActionUrlGrid == undefined ? this.sActionUrl : oOptions.sActionUrlGrid;
+    this.sObjNameGrid = oOptions.sObjNameGrid == undefined ? '' : oOptions.sObjNameGrid;
 
-    /*
-     * Note. Are needed for Grid and don't used for now.
-     * 
-    this.sObjNameGrid = oOptions.sObjNameGrid;
-    this.sParamsDivider = oOptions.sParamsDivider == undefined ? '#-#' : oOptions.sParamsDivider;
-    this.sTextSearchInput = oOptions.sTextSearchInput == undefined ? '' : oOptions.sTextSearchInput;
-    */
+    var $this = this;
+    $(document).ready(function () {
+        $this.initGrid();
+    });
 }
 
 BxDolStudioPageAgents.prototype = Object.create(BxDolStudioPage.prototype);
 BxDolStudioPageAgents.prototype.constructor = BxDolStudioPageAgents;
 
+BxDolStudioPageAgents.prototype.initGrid = function() {
+    if (typeof(glGrids) == 'undefined' || !this.sObjNameGrid || !glGrids[this.sObjNameGrid])
+        return;
+
+    glGrids[this.sObjNameGrid].setOnReload(function() {
+        document.location = document.location;
+    });
+};
+
+BxDolStudioPageAgents.prototype.agentActions = function(oSource) {
+    var iId = $(oSource).parents('.bx-agt-agent:first').attr('data-id');
+    bx_menu_popup_inline('#bx-agt-agent-actions-' + iId, oSource);
+};
+
+BxDolStudioPageAgents.prototype.agentAction = function(sAction, iId, iConfirm, iResetPaginate) {
+    $('.bx-popup-applied:visible').dolPopupHide();
+    if (typeof(glGrids) == 'undefined' || !this.sObjNameGrid || !glGrids[this.sObjNameGrid])
+        return;
+
+    glGrids[this.sObjNameGrid].actionSingle(sAction, iId, {
+        confirm: iConfirm,
+        reset_paginate: iResetPaginate
+    });
+};
+
 BxDolStudioPageAgents.prototype.agentActivate = function(oSource) {
     var oDate = new Date();
     var oAgent = $(oSource).parents('.bx-agt-agent:first');

+ 22 - 1
studio/template/agents_agents.html

@@ -18,7 +18,28 @@
                 </div>
                 <div class="bx-agt-a-ttl flex justify-between items-center gap-2 pt-1 min-w-0">
                     <div class="bx-agt-attl flex-1 min-w-0 font-semibold text-base truncate">__title__</div>
-                    <div class="bx-agt-attl-act">__switcher__</div>
+                    <div class="bx-agt-attl-act flex items-center shrink-0 gap-1">
+                        __switcher__
+                        <bx_if:show_actions>
+                            <a class="bx-agt-agent-more w-7 h-7 flex items-center justify-center rounded-full opacity-70 hover:opacity-100 hover:bg-gray-200/80 dark:hover:bg-gray-800/80" href="javascript:void(0)" onclick="__js_object__.agentActions(this)">
+                                <i class="sys-icon ellipsis-v"></i>
+                            </a>
+                            <div id="bx-agt-agent-actions-__id__" class="bx-agt-agent-actions bx-popup bx-popup-box bx-popup-color-bg bx-popup-border bx-def-round-corners" style="display:none">
+                                <ul class="bx-menu-ver bx-clearfix">
+                                    <bx_repeat:actions>
+                                        <li class="bx-menu-item bx-def-round-corners bx-def-color-bg-hl-hover bx-def-padding-sec-left">
+                                            <a href="javascript:void(0)" onclick="__js_object__.agentAction('__name__', __id__, __confirm__, __reset_paginate__)">
+                                                <div class="flex items-center p-2 truncate">
+                                                    <span class="bx-menu-item-icon bx-stl-mii">__icon__</span>
+                                                    <span class="bx-menu-item-title bx-stl-mit">__title__</span>
+                                                </div>
+                                            </a>
+                                        </li>
+                                    </bx_repeat:actions>
+                                </ul>
+                            </div>
+                        </bx_if:show_actions>
+                    </div>
                 </div>
                 <div class="bx-agt-a-dsc text-sm">__description__</div>
             </div>

+ 13 - 0
studio/template/css/agents.css

@@ -4,6 +4,19 @@
     height: 1rem;
 }
 
+.bx-agt-agent-actions .bx-menu-item-icon {
+    width: 1.25rem;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 0.5rem;
+}
+.bx-agt-agent-actions .bx-menu-item-icon svg,
+.bx-agt-agent-more svg {
+    width: 1rem;
+    height: 1rem;
+}
+
 /**--- Automators ---**/
 .cmt .bx-menu-custom.bx-menu-custom-hor > li > span {
     margin-right: 0;

+ 53 - 2
studio/template/scripts/BxBaseStudioAgents.php

@@ -276,6 +276,10 @@ class BxBaseStudioAgents extends BxDolStudioAgents
         $oAi = BxDolAI::getInstance();
         $aAgents = $oAi->getAgentsBy(['sample' => 'all']);
 
+        $oGrid = $this->getGrid($this->aGridObjects[BX_DOL_STUDIO_AGENTS_TYPE_AGENTS], true);
+        if($oGrid)
+            $this->aPageJsOptions['sObjNameGrid'] = $oGrid->getObject();
+
         $oForm = new BxTemplFormView([]);
         $aInput = [
             'type' => 'switcher',
@@ -325,8 +329,11 @@ class BxBaseStudioAgents extends BxDolStudioAgents
 
             $aInput['checked'] = (int)$aAgent['active'] != 0;
 
+            $aTmplVarsActions = $oGrid ? $this->_getAgentCardActions($oGrid, $aAgent, $oTemplate) : [];
+
             $aTmplVarsAgents[] = [
                 'id' => $aAgent['id'],
+                'js_object' => $sJsObject,
                 'icon' => $sIcon,
                 'bx_if:show_trigger' => [
                     'condition' => !empty($aTmplVarsTrigger),
@@ -342,8 +349,16 @@ class BxBaseStudioAgents extends BxDolStudioAgents
                         'unit' => $sProfile
                     ]
                 ],
-                'title' => $aAgent['title'],
+                'title' => $aAgent['title'] ?: $aAgent['name'],
                 'switcher' => $oForm->genInput($aInput),
+                'bx_if:show_actions' => [
+                    'condition' => !empty($aTmplVarsActions),
+                    'content' => [
+                        'id' => $aAgent['id'],
+                        'js_object' => $sJsObject,
+                        'bx_repeat:actions' => $aTmplVarsActions
+                    ]
+                ],
                 'description' => bx_process_output($aAgent['description'])
             ];
         }
@@ -351,11 +366,47 @@ class BxBaseStudioAgents extends BxDolStudioAgents
         return $oTemplate->parseHtmlByName('agents.html', [
             'content' => $oTemplate->parseHtmlByName('agents_agents.html', [
                 'bx_repeat:agents' => $aTmplVarsAgents,
-            ]),
+            ]) . ($oGrid ? $oGrid->getCodeJs() : ''),
             'js_content' => $this->getPageJsCode()
         ]);
     }
 
+    protected function _getAgentCardActions($oGrid, $aAgent, $oTemplate)
+    {
+        $aActions = $oGrid->getActionsRaw('single', $aAgent['id'], $aAgent);
+        if(empty($aActions) || !is_array($aActions))
+            return [];
+
+        $oIconset = BxDolIconset::getObjectInstance();
+        $oFunctions = $oTemplate->getTemplateFunctions();
+
+        $aTmplVars = [];
+        foreach($aActions as $aAction) {
+            $sIcon = '';
+            if(!empty($aAction['icon'])) {
+                list($sIconFont, $sIconUrl, $sIconA, $sIconHtml) = $oFunctions->getIcon($aAction['icon']);
+                if($sIconFont)
+                    $sIcon = $oTemplate->parseIcon($oIconset->getIcon($sIconFont));
+                else if($sIconHtml)
+                    $sIcon = $sIconHtml;
+                else if($sIconUrl)
+                    $sIcon = '<img src="' . $sIconUrl . '" />';
+            }
+
+            $aTmplVars[] = [
+                'name' => $aAction['name'],
+                'title' => $aAction['title'],
+                'icon' => $sIcon,
+                'confirm' => (int)$aAction['confirm'],
+                'reset_paginate' => (int)$aAction['reset_paginate'],
+                'id' => $aAgent['id'],
+                'js_object' => $this->getPageJsObject(),
+            ];
+        }
+
+        return $aTmplVars;
+    }
+
     protected function getAgentsGrid()
     {
         return $this->getGrid($this->aGridObjects[BX_DOL_STUDIO_AGENTS_TYPE_AGENTS]);

+ 1 - 35
template/grid.html

@@ -66,39 +66,5 @@
     </bx_if:display_footer>    
 </div>
 
-<script>
-    $(document).ready(function() {
-        if (typeof(glGrids) == 'undefined')
-            glGrids = {};
-        if (undefined == glGrids.__object__) {
-            glGrids.__object__ = new BxDolGrid('__object__', {
-                csrf_token: '__csrf_token__',
-                sortable: __sortable__,
-                sorting: __sorting__,
-                paginate_get_start: '__paginate_get_start__',
-                paginate_get_per_page: '__paginate_get_per_page__',
-                start: __start__,
-                per_page: __per_page__,
-                filter: '__filter__',
-                filter_get: '__filter_get__',
-                order_field: '__order_field__',
-                order_dir: '__order_dir__',
-                order_get_field: '__order_get_field__',
-                order_get_dir: '__order_get_dir__',
-                text_search_input: '<bx_text_js:_sys_grid_search />',
-                popup_options: __popup_options__,
-                query_append: __query_append__,
-                confirm_messages: __confirm_messages__,
-                columns: __columns__
-            });
-        }
-        else
-            glGrids.__object__.init({
-                start: __start__,
-                per_page: __per_page__,
-                filter: '__filter__',
-                query_append: __query_append__
-            });
-    });
-</script>
+<bx_include_auto:grid_js.html />
 

+ 35 - 0
template/grid_js.html

@@ -0,0 +1,35 @@
+<script>
+    $(document).ready(function() {
+        if (typeof(glGrids) == 'undefined')
+            glGrids = {};
+        if (undefined == glGrids.__object__) {
+            glGrids.__object__ = new BxDolGrid('__object__', {
+                csrf_token: '__csrf_token__',
+                sortable: __sortable__,
+                sorting: __sorting__,
+                paginate_get_start: '__paginate_get_start__',
+                paginate_get_per_page: '__paginate_get_per_page__',
+                start: __start__,
+                per_page: __per_page__,
+                filter: '__filter__',
+                filter_get: '__filter_get__',
+                order_field: '__order_field__',
+                order_dir: '__order_dir__',
+                order_get_field: '__order_get_field__',
+                order_get_dir: '__order_get_dir__',
+                text_search_input: '<bx_text_js:_sys_grid_search />',
+                popup_options: __popup_options__,
+                query_append: __query_append__,
+                confirm_messages: __confirm_messages__,
+                columns: __columns__
+            });
+        }
+        else
+            glGrids.__object__.init({
+                start: __start__,
+                per_page: __per_page__,
+                filter: '__filter__',
+                query_append: __query_append__
+            });
+    });
+</script>

+ 106 - 0
template/scripts/BxBaseGrid.php

@@ -326,6 +326,112 @@ class BxBaseGrid extends BxDolGrid
         return $this->_oTemplate->parseHtmlByName('grid.html', $aVars);
     }
 
+    protected function _getJsCodeVars()
+    {
+        $sFilter = $this->_getFilterValue();
+        $sOrderField = $this->_getOrderValue();
+        $sOrderDir = 0 === strcasecmp('desc', bx_get($this->_aOptions['order_get_dir'])) ? 'DESC' : 'ASC';
+
+        $iStart = 0;
+        if($this->_aOptions['paginate_get_start'] && ($iStartGet = (int)bx_get($this->_aOptions['paginate_get_start'])) >= 0)
+            $iStart = $iStartGet;
+
+        $iPerPage = 10;
+        if($this->_aOptions['paginate_get_per_page'] && ($iPerPageGet = (int)bx_get($this->_aOptions['paginate_get_per_page'])) > 0)
+            $iPerPage = $iPerPageGet;
+        else if($this->_aOptions['paginate_per_page'])
+            $iPerPage = (int)$this->_aOptions['paginate_per_page'];
+
+        $sPopupOptions = '{}';
+        if (!empty($this->_aPopupOptions) && is_array($this->_aPopupOptions))
+            $sPopupOptions = json_encode($this->_aPopupOptions);
+
+        $aQueryAppend = array_merge(is_array($this->_aQueryAppend) ? $this->_aQueryAppend : array(), is_array($this->_aMarkers) ? $this->_aMarkers : array());
+        if(!empty($this->_aQueryAppendExclude) && is_array($this->_aQueryAppendExclude))
+            $aQueryAppend = array_diff_key($aQueryAppend, array_flip($this->_aQueryAppendExclude));
+
+        $sQueryAppend = '{}';
+        if (!empty($aQueryAppend) && is_array($aQueryAppend))
+            $sQueryAppend = json_encode($aQueryAppend);
+
+        $sConfirmMessages = '{}';
+        if (!empty($this->_aConfirmMessages) && is_array($this->_aConfirmMessages))
+            $sConfirmMessages = json_encode($this->_aConfirmMessages);
+
+        return [
+            'object' => $this->_sObject,
+            'csrf_token' => BxDolForm::getCsrfToken(),
+            'sortable' => empty($this->_aOptions['field_order']) ? 0 : 1,
+            'sorting' => empty($this->_getOrderFields()) ? 0 : 1,
+            'paginate_get_start' => $this->_aOptions['paginate_get_start'],
+            'paginate_get_per_page' => $this->_aOptions['paginate_get_per_page'],
+            'start' => $iStart,
+            'per_page' => $iPerPage,
+            'filter' => bx_js_string($sFilter, BX_ESCAPE_STR_APOS),
+            'filter_get' => bx_js_string($this->_aOptions['filter_get']),
+            'order_field' => bx_js_string($sOrderField, BX_ESCAPE_STR_APOS),
+            'order_dir' => bx_js_string($sOrderDir, BX_ESCAPE_STR_APOS),
+            'order_get_field' => bx_js_string($this->_aOptions['order_get_field']),
+            'order_get_dir' => bx_js_string($this->_aOptions['order_get_dir']),
+            'popup_options' => $sPopupOptions,
+            'query_append' => $sQueryAppend,
+            'confirm_messages' => $sConfirmMessages,
+            'columns' => count($this->_aOptions['fields']),
+        ];
+    }
+
+    /**
+     * Init grid JS class without rendering grid HTML.
+     * @return string
+     */
+    public function getCodeJs()
+    {
+        $this->_replaceMarkers();
+        $this->_addJsCss();
+
+        return $this->_oTemplate->parseHtmlByName('grid_js.html', $this->_getJsCodeVars());
+    }
+
+    /**
+     * Get raw actions list (name, title, icon, confirm) without HTML.
+     * Custom `_getAction[Name]` methods that return empty hide the action, same as in the grid.
+     * @param string $sType action type: single, bulk, independent
+     * @param mixed $sActionData row id for single actions
+     * @param array $aRow row data, used by custom action methods
+     * @return array
+     */
+    public function getActionsRaw($sType, $sActionData = false, $aRow = [])
+    {
+        $sActionsType = 'actions_' . $sType;
+        if (empty($this->_aOptions[$sActionsType]) || !is_array($this->_aOptions[$sActionsType]))
+            return [];
+
+        $aResult = [];
+        foreach ($this->_aOptions[$sActionsType] as $sKey => $a) {
+            if(!$a['active'] || (!$a['title'] && !$a['icon']))
+                continue;
+
+            $sFunc = '_getAction' . $this->_genMethodName($sKey);
+            if (method_exists($this, $sFunc)) {
+                if (!isset($a['attr']))
+                    $a['attr'] = [];
+                $mixed = $this->$sFunc($sType, $sKey, $a, false, false, $aRow);
+                if ($mixed === '' || $mixed === false)
+                    continue;
+            }
+
+            $aResult[] = [
+                'name' => $sKey,
+                'title' => $a['title'],
+                'icon' => !empty($a['icon']) ? $a['icon'] : '',
+                'confirm' => !empty($a['confirm']) ? 1 : 0,
+                'reset_paginate' => false === strpos($sKey, 'delete') ? 0 : 1,
+            ];
+        }
+
+        return $aResult;
+    }
+
     /**
      * Get grid code API.
      * @return array