Plugin Directory

Changeset 3476690


Ignore:
Timestamp:
03/06/2026 08:28:45 PM (4 weeks ago)
Author:
trainingbusinesspros
Message:

Update to version 4.3.1 from GitHub

Location:
groundhogg
Files:
40 edited
1 copied

Legend:

Unmodified
Added
Removed
  • groundhogg/tags/4.3.1/README.txt

    r3464589 r3476690  
    77Tested up to: 6.9
    88Requires PHP: 7.1
    9 Stable tag: 4.3
     9Stable tag: 4.3.1
    1010License: GPLv3
    1111License URI: https://www.gnu.org/licenses/gpl.md
     
    379379== Changelog ==
    380380
     381= 4.3.1 (2026-03-06) =
     382* ADDED Quarter time ranges (This Quarter, Next Quarter, Last Quarter) for date filters.
     383* ADDED Decimal support for number fields.
     384* ADDED View profile button to the simulator contact card.
     385* FIXED Missing support for event arguments in the simulator (backend magic wizardy).
     386* FIXED Flow template categories and search feature not working if the toolbar widget was disabled.
     387
    381388= 4.3 (2026-02-13) =
    382389* ADDED 🌐 Global blocks (email template parts) in the email editor.
    383  * Save any block as a Global Block
    384  * Are synced across all your templates
    385  * Can be edited inline
    386  * Can be detached to edit separately
    387  * Can be exported and imported
     390 * Save any block as a Global Block.
     391 * Are synced across all your templates.
     392 * Can be edited inline.
     393 * Can be detached to edit separately.
     394 * Can be exported and imported.
    388395* TWEAKED Editor design
    389  * Reduced block toolbar icon sizes
    390  * Added a container border for container blocks in the block inspector
     396 * Reduced block toolbar icon sizes.
     397 * Added a container border for container blocks in the block inspector.
    391398
    392399= 4.2.12 (2026-02-10) =
  • groundhogg/tags/4.3.1/admin/broadcasts/broadcasts-page.php

    r3394550 r3476690  
    5050        }
    5151
    52         $total_contacts  = get_post_var( 'total_contacts' );
    53         $amount          = get_post_var( 'batch_amount' );
    54         $interval        = get_post_var( 'batch_interval' );
    55         $interval_length = get_post_var( 'batch_interval_length' );
     52        $total_contacts  = absint( get_post_var( 'total_contacts' ) );
     53        $amount          = absint( get_post_var( 'batch_amount' ) );
     54        $interval        = absint( get_post_var( 'batch_interval' ) );
     55        $interval_length = absint( get_post_var( 'batch_interval_length' ) );
    5656
    5757        $batches = floor( $total_contacts / $amount );
  • groundhogg/tags/4.3.1/admin/funnels/funnels-page.php

    r3394550 r3476690  
    273273            case 'add':
    274274                wp_enqueue_style( 'groundhogg-admin-element' );
     275                wp_enqueue_script( 'groundhogg-admin-data' ); // needed for createState
    275276                break;
    276277            case 'view':
  • groundhogg/tags/4.3.1/admin/funnels/simulator.php

    r3386122 r3476690  
    1313use function Groundhogg\array_find;
    1414use function Groundhogg\Cli\doing_cli;
     15use function Groundhogg\get_array_var;
    1516use function Groundhogg\get_object_ids;
    1617use function Groundhogg\the_funnel;
     
    2728    protected static $steps = [];
    2829    protected static $is_dry_run = true;
     30
     31    /**
     32     * @var Event
     33     */
     34    protected static $curr_event;
     35
     36    /**
     37     * Allow for the persistence of event arguments during simulation
     38     *
     39     * @var array
     40     */
     41    protected static $event_args = [];
     42
     43    /**
     44     * Allow adding new event args
     45     *
     46     * @param array $args
     47     *
     48     * @return void
     49     */
     50    public static function set_event_args( $args ) {
     51        self::$event_args = array_merge( self::$event_args, $args );
     52    }
     53
     54    /**
     55     * Get an arg from the args array
     56     *
     57     * @param string $arg
     58     * @param        $default
     59     *
     60     * @return bool|mixed
     61     */
     62    public static function get_event_arg( string $arg, $default = false ) {
     63        return get_array_var( self::$event_args, $arg, $default );
     64    }
    2965
    3066    /**
     
    264300                    ] );
    265301
     302                    // add the event args here also
     303                    $event->set_args( self::$event_args );
     304
    266305                    if ( $current->type_is( Send_Email::TYPE ) ) {
    267306                        $event->update( [ 'email_id' => $current->get_meta( 'email_id' ) ] );
     
    271310                    $current->get_step_element()->pre_run( $contact, $event );
    272311                    $result = $current->get_step_element()->run( $contact, $event );
     312
     313                    // update event args if there were any changes
     314                    $event->set_args( self::$event_args );
     315
    273316                    if ( is_wp_error( $result ) ) {
    274317                        self::log( sprintf( "⚠️ %s", $result->get_error_message() ) );
  • groundhogg/tags/4.3.1/admin/reports/views/contacts.php

    r3343709 r3476690  
    22
    33namespace Groundhogg\Admin\Reports\Views;
     4
     5use function Groundhogg\html;
    46
    57if ( ! defined( 'ABSPATH' ) ) {
     
    5456            <h2 class="title">
    5557                <?php esc_html_e( 'Unsubscribe Reasons', 'groundhogg' ); ?>
    56                 <span class="gh-has-tooltip dashicons dashicons-info">
    57                     <span class="gh-tooltip top">
    58                         <?php esc_html_e( 'This chart shows individual unsubscribe events with their reasons, which may be larger that the total number of unsubscribed contacts.', 'groundhogg' ) ?>
    59                     </span>
    60                 </span>
     58                <?php html()->info_tooltip( esc_html( __( 'This chart shows individual unsubscribe events with their reasons, which may be larger that the total number of unsubscribed contacts.', 'groundhogg' ) ), 'top', true ); ?>
    6159            </h2>
    6260        </div>
  • groundhogg/tags/4.3.1/assets/js/admin/components/properties.js

    r3352650 r3476690  
    7878    number: {
    7979      name: __('Number', 'groundhogg'),
    80       view: ({ label, ...props }) => {
     80      view: ({ label, decimals = 0, ...props }) => {
     81
     82        let step = (1 / Math.pow(10, decimals)).toString()
    8183        //language=HTML
    8284        return `<label class="property-label" for="${props.id}">${label}</label>${input({
    83             className: 'full-width', ...props, type: 'number',
     85            className: 'full-width', ...props, type: 'number', step
    8486        })}`
     87      },
     88      edit: (field) => {
     89
     90        const { decimals = 0 } = field
     91
     92        //language=HTML
     93        return `
     94            <div class="gh-rows-and-columns">
     95                <div class="gh-row">
     96                    <div class="gh-col">
     97                        <label>${__('Decimals', 'groundhogg')}</label>
     98                        ${input({
     99                type: 'number',
     100                name: 'property_number_decimals',
     101                id: 'property-number-decimals',
     102                value: decimals,
     103                min: 0,
     104                max: 100,
     105            })}
     106                    </div>
     107                </div>
     108            </div>
     109        `
     110      },
     111      onEditMount: (field, updateField) => {
     112        $('#property-number-decimals').on( 'change', (e) => {
     113          updateField({
     114            decimals: e.target.value,
     115          })
     116        })
    85117      },
    86118    },
  • groundhogg/tags/4.3.1/assets/js/admin/components/properties.min.js

    r3352650 r3476690  
    1 ($=>{const{uuid,specialChars,icons,modal,copyObject,input,textarea,select,tinymceElement,moreMenu,tooltip,inputRepeaterWidget,confirmationModal,dangerConfirmationModal,toggle}=Groundhogg.element;const{sprintf,__,_x,_n}=wp.i18n;const optionsRepeater=({selector,options,onChange})=>{inputRepeaterWidget({selector:selector,rows:options.map(o=>[o]),cellCallbacks:[input],cellProps:[{placeholder:__("Option")}],sortable:true,onChange:r=>{onChange(r.map(r=>r[0]))}}).mount()};const standardField={onMount:({id,name},onChange)=>{$(`#${id}`).on("change",e=>{onChange({[name]:e.target.value})})},edit:()=>{return""},onEditMount:()=>{}};const getFieldType=type=>{return{...standardField,...fieldTypes[type]}};const fieldTypes={text:{name:__("Text","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"text"})}`}},textarea:{name:__("Textarea","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${textarea({className:"full-width",...props})}`}},number:{name:__("Number","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"number"})}`}},url:{name:__("URL","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>
     1($=>{const{uuid,specialChars,icons,modal,copyObject,input,textarea,select,tinymceElement,moreMenu,tooltip,inputRepeaterWidget,confirmationModal,dangerConfirmationModal,toggle}=Groundhogg.element;const{sprintf,__,_x,_n}=wp.i18n;const optionsRepeater=({selector,options,onChange})=>{inputRepeaterWidget({selector:selector,rows:options.map(o=>[o]),cellCallbacks:[input],cellProps:[{placeholder:__("Option")}],sortable:true,onChange:r=>{onChange(r.map(r=>r[0]))}}).mount()};const standardField={onMount:({id,name},onChange)=>{$(`#${id}`).on("change",e=>{onChange({[name]:e.target.value})})},edit:()=>{return""},onEditMount:()=>{}};const getFieldType=type=>{return{...standardField,...fieldTypes[type]}};const fieldTypes={text:{name:__("Text","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"text"})}`}},textarea:{name:__("Textarea","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${textarea({className:"full-width",...props})}`}},number:{name:__("Number","groundhogg"),view:({label,decimals=0,...props})=>{let step=(1/Math.pow(10,decimals)).toString();return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"number",step:step})}`},edit:field=>{const{decimals=0}=field;return`
     2            <div class="gh-rows-and-columns">
     3                <div class="gh-row">
     4                    <div class="gh-col">
     5                        <label>${__("Decimals","groundhogg")}</label>
     6                        ${input({type:"number",name:"property_number_decimals",id:"property-number-decimals",value:decimals,min:0,max:100})}
     7                    </div>
     8                </div>
     9            </div>
     10        `},onEditMount:(field,updateField)=>{$("#property-number-decimals").on("change",e=>{updateField({decimals:e.target.value})})}},url:{name:__("URL","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>
    211        <div class="gh-input-group">${input({className:"full-width",...props,type:"url"})}
    312            <button id="link-${props.id}" class="gh-button secondary small icon">
     
    2433                </div>
    2534            </div>
    26         `},onEditMount:(field,updateField)=>{optionsRepeater({selector:"#property-dropdown-options",options:field.options||[""],onChange:options=>updateField({options:options})})}},dropdown:{name:__("Dropdown","groundhogg"),view:({label,value,options:options=[],...props})=>{options=options.map(o=>({text:o,value:o}));options.unshift({text:__("Select..."),value:""});if(props.multiple&&!Array.isArray(value)){value=value.split(",").map(v=>v.trim())}return`<label class="property-label" for="${props.id}">${label}</label>${select({className:"full-width",options:options,selected:value,...props})}`},onMount:({id,multiple,name,...props},onChange)=>{$(`#${id}`).on("change",e=>{if(multiple){onChange({[name]:[...e.target.selectedOptions].map(o=>o.value)})}else{onChange({[name]:e.target.value})}})},edit:field=>{const{multiple}=field;return`
     35        `},onEditMount:(field,updateField)=>{optionsRepeater({selector:"#property-dropdown-options",options:field.options||[""],onChange:options=>updateField({options:options})})}},dropdown:{name:__("Dropdown","groundhogg"),view:({label,value,options=[],...props})=>{options=options.map(o=>({text:o,value:o}));options.unshift({text:__("Select..."),value:""});if(props.multiple&&!Array.isArray(value)){value=value.split(",").map(v=>v.trim())}return`<label class="property-label" for="${props.id}">${label}</label>${select({className:"full-width",options:options,selected:value,...props})}`},onMount:({id,multiple,name,...props},onChange)=>{$(`#${id}`).on("change",e=>{if(multiple){onChange({[name]:[...e.target.selectedOptions].map(o=>o.value)})}else{onChange({[name]:e.target.value})}})},edit:field=>{const{multiple}=field;return`
    2736            <div class="gh-rows-and-columns">
    2837                <div class="gh-row">
     
    4150          <button id="add-custom-property" class="gh-button secondary">
    4251              ${__("Add custom properties","groundhogg")}
    43           </button>`},groups:({groups,fields:fields=[]},editable)=>{return`
     52          </button>`},groups:({groups,fields=[]},editable)=>{return`
    4453          <div class="property-groups">
    4554              ${groups.map(g=>Templates.group(g,fields.filter(f=>f.group==g.id),editable)).join("")}
     
    7281                  </button>
    7382              </div>
    74           </div>`},addField:field=>{const{type,label,name,id,order:order=10,width:width=2}=field;let editUI="";try{editUI=getFieldType(type).edit(field)}catch(e){console.log(e)}return`
     83          </div>`},addField:field=>{const{type,label,name,id,order=10,width=2}=field;let editUI="";try{editUI=getFieldType(type).edit(field)}catch(e){console.log(e)}return`
    7584          <div class="property-field">
    7685              <h3 class="no-margin-top">${id?__("Edit field","groundhogg"):__("Add field","groundhogg")}</h3>
     
    123132                  </div>
    124133              </div>
    125           </div>`},field:({group,...field})=>{let fieldUI;try{fieldUI=getFieldType(field.type).view(field)}catch(e){console.log(e);fieldUI=`<span class="gh-text danger">${__("This field is corrupted","groundhogg")}</span>`}let{width:width=2}=field;return`
     134          </div>`},field:({group,...field})=>{let fieldUI;try{fieldUI=getFieldType(field.type).view(field)}catch(e){console.log(e);fieldUI=`<span class="gh-text danger">${__("This field is corrupted","groundhogg")}</span>`}let{width=2}=field;return`
    126135          <div class="property-field col-width-${width}" data-group="${group}" data-id="${field.id}">
    127136              ${fieldUI}
    128           </div>`}};const Properties=(selector,{properties:properties={groups:[],fields:[]},values:values={},onPropertiesUpdated:onPropertiesUpdated=properties=>{},onChange:onChange=properties=>{},canEdit:canEdit=()=>true})=>{properties=copyObject(properties);values=copyObject(values);function isInternalNameInUse(name,fieldId){if(!properties||typeof properties.fields==="undefined"){return false}return properties.fields.some(field=>field.name===name&&field.id!==fieldId)}const removeGroup=id=>{const{fields:fields=[],groups:groups=[]}=properties;properties={...properties,groups:[...groups.filter(g=>g.id!=id)],fields:[...fields.filter(f=>f.group!=id)]};onPropertiesUpdated(properties);mount()};const removeField=id=>{properties={...properties,fields:[...properties.fields.filter(f=>f.id!=id)]};onPropertiesUpdated(properties);mount()};const addField=field=>{if(!properties.fields){properties.fields=[]}properties.fields.push({...field,id:uuid()});onPropertiesUpdated(properties);mount()};const editField=(fieldId,field)=>{properties.fields=[...properties.fields.map(f=>f.id===fieldId?field:f)];onPropertiesUpdated(properties);mount()};const editGroup=(groupId,group)=>{properties.groups=[...properties.groups.map(g=>g.id===groupId?{...g,...group}:g)];onPropertiesUpdated(properties);mount()};const moveGroup=(groupId,direction="down")=>{let group=properties.groups.find(g=>g.id==groupId);let index=properties.groups.findIndex(g=>g.id==groupId);properties.groups.splice(index,1);properties.groups.splice(direction==="down"?index+1:index-1,0,group);onPropertiesUpdated(properties);mount()};const addGroup=name=>{if(!properties.groups){properties.groups=[]}let groupId=uuid();properties.groups.push({id:groupId,name:name});onPropertiesUpdated(properties);mount();addOrEditField({type:"text",name:"",label:"",group:groupId},addField)};const addPropertyGroupModal=()=>{const{close}=modal({content:Templates.addPropertyGroup()});let groupName;$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();addGroup(groupName)}})};const renamePropertyGroupModal=groupId=>{let groupName=properties.groups.find(g=>g.id==groupId).name;const{close}=modal({content:Templates.renamePropertyGroup(groupName)});$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();editGroup(groupId,{name:groupName})}})};const addOrEditField=(newField,onDone)=>{let origField=Groundhogg.functions.jsonCopy(newField);const onAddFieldMount=()=>{const sanitizeKey=label=>{return label.toLowerCase().replace(/[^a-z0-9]/g,"_")};const updateField=(props,r=false)=>{newField={...newField,...props};if(r){setContent(Templates.addField(newField));onAddFieldMount()}};$("#property-field-label").on("input change",e=>{let label=e.target.value;if(onDone===addField){let name=sanitizeKey(label);updateField({label:label,name:name});$("#property-field-name").val(name)}else{updateField({label:label})}});$("#property-field-name").on("input change",e=>{updateField({name:e.target.value})});$("#property-field-order").on("input change",e=>{updateField({order:parseInt(e.target.value)})});$("#property-field-width").on("change",e=>{updateField({width:parseInt(e.target.value)})});$("#property-field-group").select2({data:properties.groups.map(({id,name})=>({id:id,text:name,selected:newField.group===id})),multiple:false}).on("change",e=>newField.group=e.target.value);$("#property-field-type").on("change",e=>{newField.type=e.target.value;setContent(Templates.addField(newField));onAddFieldMount();$("#property-field-type").focus()});$("#create-property-field").on("click",e=>{if(isInternalNameInUse(newField.name,newField.id)){Groundhogg.element.errorDialog({message:`The internal name <code>${newField.name}</code> is already in use.`});return}if(onDone!==addField&&newField.name!==origField.name){dangerConfirmationModal({alert:`<p>Changing the internal name of a custom field might break replacement code references and other uses.</p>
    129               <p>Are you sure you want to continue?</p>`,confirmText:"Continue",onConfirm:()=>{onDone(newField);close()},onCancel:()=>{updateField({name:origField.name});$("#property-field-name").val(origField.name)}});return}onDone(newField);close()});try{getFieldType(newField.type).onEditMount(newField,updateField)}catch(e){console.log(e)}};const{close,setContent}=modal({content:Templates.addField(newField)});onAddFieldMount()};const mount=()=>{if(!properties||!properties.groups||!properties.groups.length){if(!canEdit()){return}$(selector).html(Templates.noProperties());$("#add-custom-property").on("click",e=>{e.preventDefault();addPropertyGroupModal()});return}const{fields:fields=[]}=properties;$(selector).html(Templates.groups({...properties,fields:fields.map(f=>({...f,value:values[f.name]||""}))},canEdit()));onMount()};const onMount=()=>{const{fields:fields=[]}=properties;fields.forEach(f=>{try{getFieldType(f.type).onMount(f,props=>{values={...values,...props};onChange(props)})}catch(e){console.log(e)}});$(".property-field").on("dblclick",e=>{if(!canEdit()){return}moreMenu(e.currentTarget,{items:[{key:"edit",text:__("Edit field","groundhogg")},{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{const fieldId=e.currentTarget.dataset.id;let field=properties.fields.find(f=>f.id==fieldId);switch(k){case"edit":addOrEditField({...field},field=>{editField(fieldId,field)});break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property?","groundhogg")}</p>`,onConfirm:()=>{removeField(fieldId)}});break}}})});$(".property-group-add-field").on("click",e=>{if(!canEdit()){return}const groupId=e.currentTarget.dataset.id;let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField)});$(".property-group-more").on("click",e=>{const groupId=e.currentTarget.dataset.id;let index=properties.groups.findIndex(g=>g.id==groupId);moreMenu(e.currentTarget,{items:[{key:"add-field",text:__("Add Field","groundhogg")},{key:"add-group",text:__("Add Group","groundhogg")},{key:"edit-fields",text:__("Edit Fields","groundhogg")},{key:"rename",text:__("Rename")},index!==0?{key:"move_up",text:__("Move up","groundhogg")}:null,index<properties.groups.length-1?{key:"move_down",text:__("Move down","groundhogg")}:null,{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{switch(k){case"edit-fields":confirmationModal({alert:`<p>${__("Double click a field to edit it!","groundhogg")}</p>`,confirmText:__("Got it!","groundhogg"),closeText:""});break;case"move_up":moveGroup(groupId,"up");break;case"move_down":moveGroup(groupId,"down");break;case"add-group":addPropertyGroupModal();break;case"add-field":let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField);break;case"rename":renamePropertyGroupModal(groupId);break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property group?","groundhogg")}</p>`,onConfirm:()=>{removeGroup(groupId)}});break}}})})};mount()};Groundhogg.propertiesEditor=Properties})(jQuery);
     137          </div>`}};const Properties=(selector,{properties={groups:[],fields:[]},values={},onPropertiesUpdated=properties=>{},onChange=properties=>{},canEdit=()=>true})=>{properties=copyObject(properties);values=copyObject(values);function isInternalNameInUse(name,fieldId){if(!properties||typeof properties.fields==="undefined"){return false}return properties.fields.some(field=>field.name===name&&field.id!==fieldId)}const removeGroup=id=>{const{fields=[],groups=[]}=properties;properties={...properties,groups:[...groups.filter(g=>g.id!=id)],fields:[...fields.filter(f=>f.group!=id)]};onPropertiesUpdated(properties);mount()};const removeField=id=>{properties={...properties,fields:[...properties.fields.filter(f=>f.id!=id)]};onPropertiesUpdated(properties);mount()};const addField=field=>{if(!properties.fields){properties.fields=[]}properties.fields.push({...field,id:uuid()});onPropertiesUpdated(properties);mount()};const editField=(fieldId,field)=>{properties.fields=[...properties.fields.map(f=>f.id===fieldId?field:f)];onPropertiesUpdated(properties);mount()};const editGroup=(groupId,group)=>{properties.groups=[...properties.groups.map(g=>g.id===groupId?{...g,...group}:g)];onPropertiesUpdated(properties);mount()};const moveGroup=(groupId,direction="down")=>{let group=properties.groups.find(g=>g.id==groupId);let index=properties.groups.findIndex(g=>g.id==groupId);properties.groups.splice(index,1);properties.groups.splice(direction==="down"?index+1:index-1,0,group);onPropertiesUpdated(properties);mount()};const addGroup=name=>{if(!properties.groups){properties.groups=[]}let groupId=uuid();properties.groups.push({id:groupId,name:name});onPropertiesUpdated(properties);mount();addOrEditField({type:"text",name:"",label:"",group:groupId},addField)};const addPropertyGroupModal=()=>{const{close}=modal({content:Templates.addPropertyGroup()});let groupName;$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();addGroup(groupName)}})};const renamePropertyGroupModal=groupId=>{let groupName=properties.groups.find(g=>g.id==groupId).name;const{close}=modal({content:Templates.renamePropertyGroup(groupName)});$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();editGroup(groupId,{name:groupName})}})};const addOrEditField=(newField,onDone)=>{let origField=Groundhogg.functions.jsonCopy(newField);const onAddFieldMount=()=>{const sanitizeKey=label=>{return label.toLowerCase().replace(/[^a-z0-9]/g,"_")};const updateField=(props,r=false)=>{newField={...newField,...props};if(r){setContent(Templates.addField(newField));onAddFieldMount()}};$("#property-field-label").on("input change",e=>{let label=e.target.value;if(onDone===addField){let name=sanitizeKey(label);updateField({label:label,name:name});$("#property-field-name").val(name)}else{updateField({label:label})}});$("#property-field-name").on("input change",e=>{updateField({name:e.target.value})});$("#property-field-order").on("input change",e=>{updateField({order:parseInt(e.target.value)})});$("#property-field-width").on("change",e=>{updateField({width:parseInt(e.target.value)})});$("#property-field-group").select2({data:properties.groups.map(({id,name})=>({id:id,text:name,selected:newField.group===id})),multiple:false}).on("change",e=>newField.group=e.target.value);$("#property-field-type").on("change",e=>{newField.type=e.target.value;setContent(Templates.addField(newField));onAddFieldMount();$("#property-field-type").focus()});$("#create-property-field").on("click",e=>{if(isInternalNameInUse(newField.name,newField.id)){Groundhogg.element.errorDialog({message:`The internal name <code>${newField.name}</code> is already in use.`});return}if(onDone!==addField&&newField.name!==origField.name){dangerConfirmationModal({alert:`<p>Changing the internal name of a custom field might break replacement code references and other uses.</p>
     138              <p>Are you sure you want to continue?</p>`,confirmText:"Continue",onConfirm:()=>{onDone(newField);close()},onCancel:()=>{updateField({name:origField.name});$("#property-field-name").val(origField.name)}});return}onDone(newField);close()});try{getFieldType(newField.type).onEditMount(newField,updateField)}catch(e){console.log(e)}};const{close,setContent}=modal({content:Templates.addField(newField)});onAddFieldMount()};const mount=()=>{if(!properties||!properties.groups||!properties.groups.length){if(!canEdit()){return}$(selector).html(Templates.noProperties());$("#add-custom-property").on("click",e=>{e.preventDefault();addPropertyGroupModal()});return}const{fields=[]}=properties;$(selector).html(Templates.groups({...properties,fields:fields.map(f=>({...f,value:values[f.name]||""}))},canEdit()));onMount()};const onMount=()=>{const{fields=[]}=properties;fields.forEach(f=>{try{getFieldType(f.type).onMount(f,props=>{values={...values,...props};onChange(props)})}catch(e){console.log(e)}});$(".property-field").on("dblclick",e=>{if(!canEdit()){return}moreMenu(e.currentTarget,{items:[{key:"edit",text:__("Edit field","groundhogg")},{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{const fieldId=e.currentTarget.dataset.id;let field=properties.fields.find(f=>f.id==fieldId);switch(k){case"edit":addOrEditField({...field},field=>{editField(fieldId,field)});break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property?","groundhogg")}</p>`,onConfirm:()=>{removeField(fieldId)}});break}}})});$(".property-group-add-field").on("click",e=>{if(!canEdit()){return}const groupId=e.currentTarget.dataset.id;let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField)});$(".property-group-more").on("click",e=>{const groupId=e.currentTarget.dataset.id;let index=properties.groups.findIndex(g=>g.id==groupId);moreMenu(e.currentTarget,{items:[{key:"add-field",text:__("Add Field","groundhogg")},{key:"add-group",text:__("Add Group","groundhogg")},{key:"edit-fields",text:__("Edit Fields","groundhogg")},{key:"rename",text:__("Rename")},index!==0?{key:"move_up",text:__("Move up","groundhogg")}:null,index<properties.groups.length-1?{key:"move_down",text:__("Move down","groundhogg")}:null,{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{switch(k){case"edit-fields":confirmationModal({alert:`<p>${__("Double click a field to edit it!","groundhogg")}</p>`,confirmText:__("Got it!","groundhogg"),closeText:""});break;case"move_up":moveGroup(groupId,"up");break;case"move_down":moveGroup(groupId,"down");break;case"add-group":addPropertyGroupModal();break;case"add-field":let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField);break;case"rename":renamePropertyGroupModal(groupId);break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property group?","groundhogg")}</p>`,onConfirm:()=>{removeGroup(groupId)}});break}}})})};mount()};Groundhogg.propertiesEditor=Properties})(jQuery);
  • groundhogg/tags/4.3.1/assets/js/admin/filters/filters.js

    r3269144 r3476690  
    153153
    154154  const pastDateRanges = {
    155     'any'       : __('At any time', 'groundhogg'),
    156     'today'     : __('Today', 'groundhogg'),
    157     'yesterday' : __('Yesterday', 'groundhogg'),
    158     'this_week' : __('This week', 'groundhogg'),
    159     'last_week' : __('Last week', 'groundhogg'),
    160     'this_month': __('This month', 'groundhogg'),
    161     'last_month': __('Last month', 'groundhogg'),
    162     'this_year' : __('This year', 'groundhogg'),
    163     '24_hours'  : __('In the last 24 hours', 'groundhogg'),
    164     '7_days'    : __('In the last 7 days', 'groundhogg'),
    165     '14_days'   : __('In the last 14 days', 'groundhogg'),
    166     '30_days'   : __('In the last 30 days', 'groundhogg'),
    167     '60_days'   : __('In the last 60 days', 'groundhogg'),
    168     '90_days'   : __('In the last 90 days', 'groundhogg'),
    169     '365_days'  : __('In the last 365 days', 'groundhogg'),
    170     'x_days'    : __('In the last X days', 'groundhogg'),
    171     'before'    : __('Before', 'groundhogg'),
    172     'after'     : __('After', 'groundhogg'),
    173     'between'   : __('Between', 'groundhogg'),
    174     'day_of'    : __('Day of', 'groundhogg'),
     155    'any'         : __('At any time', 'groundhogg'),
     156    'today'       : __('Today', 'groundhogg'),
     157    'yesterday'   : __('Yesterday', 'groundhogg'),
     158    'this_week'   : __('This week', 'groundhogg'),
     159    'last_week'   : __('Last week', 'groundhogg'),
     160    'this_month'  : __('This month', 'groundhogg'),
     161    'last_month'  : __('Last month', 'groundhogg'),
     162    'this_quarter': __('This quarter', 'groundhogg'),
     163    'last_quarter': __('Last quarter', 'groundhogg'),
     164    'this_year'   : __('This year', 'groundhogg'),
     165    'last_year'   : __('Last year', 'groundhogg'),
     166    '24_hours'    : __('In the last 24 hours', 'groundhogg'),
     167    '7_days'      : __('In the last 7 days', 'groundhogg'),
     168    '14_days'     : __('In the last 14 days', 'groundhogg'),
     169    '30_days'     : __('In the last 30 days', 'groundhogg'),
     170    '60_days'     : __('In the last 60 days', 'groundhogg'),
     171    '90_days'     : __('In the last 90 days', 'groundhogg'),
     172    '365_days'    : __('In the last 365 days', 'groundhogg'),
     173    'x_days'      : __('In the last X days', 'groundhogg'),
     174    'before'      : __('Before', 'groundhogg'),
     175    'after'       : __('After', 'groundhogg'),
     176    'between'     : __('Between', 'groundhogg'),
     177    'day_of'      : __('Day of', 'groundhogg'),
    175178  }
    176179
     
    180183    'tomorrow'     : __('Tomorrow', 'groundhogg'),
    181184    'this_week'    : __('This week', 'groundhogg'),
     185    'next_week'    : __('Next week', 'groundhogg'),
    182186    'this_month'   : __('This month', 'groundhogg'),
     187    'next_month'   : __('Next month', 'groundhogg'),
     188    'this_quarter' : __('This quarter', 'groundhogg'),
     189    'next_quarter' : __('Next quarter', 'groundhogg'),
    183190    'this_year'    : __('This year', 'groundhogg'),
     191    'next_year'    : __('Next year', 'groundhogg'),
    184192    'next_24_hours': __('In the next 24 hours', 'groundhogg'),
    185193    'next_7_days'  : __('In the next 7 days', 'groundhogg'),
  • groundhogg/tags/4.3.1/assets/js/admin/filters/filters.min.js

    r3269144 r3476690  
    1 ($=>{const{searchOptionsWidget,bold,uuid,regexp,andList,clickedIn,orList}=Groundhogg.element;const{Div,Button,ItemPicker,Fragment,Input,Select,Span,Ellipses,Dashicon,InputGroup,ToolTip}=MakeEl;const{formatNumber,formatTime,formatDate,formatDateTime}=Groundhogg.formatting;const{sprintf,__,_x,_n}=wp.i18n;const{base64_json_encode}=Groundhogg.functions;const AllComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const StringComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const NumericComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg")};const ComparisonsTitleGenerators={equals:(k,v)=>sprintf(_x("%1$s equals %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_equals:(k,v)=>sprintf(_x("%1$s does not equal %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),contains:(k,v)=>sprintf(_x("%1$s contains %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_contains:(k,v)=>sprintf(_x("%1$s does not contain %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),starts_with:(k,v)=>sprintf(_x("%1$s starts with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),ends_with:(k,v)=>sprintf(_x("%1$s ends with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_start_with:(k,v)=>sprintf(_x("%1$s does not start with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_end_with:(k,v)=>sprintf(_x("%1$s does not end with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than:(k,v)=>sprintf(_x("%1$s is less than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is less than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than:(k,v)=>sprintf(_x("%1$s is greater than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is greater than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),in:(k,v)=>sprintf(_x("%1$s is %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_in:(k,v)=>sprintf(_x("%1$s is not %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),empty:(k,v)=>sprintf(_x("%1$s is empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_empty:(k,v)=>sprintf(_x("%1$s is not empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),includes:(k,v)=>sprintf(_x("%1$s includes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),excludes:(k,v)=>sprintf(_x("%1$s excludes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),before:(k,v)=>sprintf(_x("%1$s is before %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),day_of:(k,v)=>sprintf(_x("%1$s on %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),after:(k,v)=>sprintf(_x("%1$s is after %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),between:(k,v,v2)=>sprintf(_x("%1$s is between %2$s and %3$s","%1 is a key and %2 and %3 are user defined values","groundhogg"),k,v,v2)};const pastDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),yesterday:__("Yesterday","groundhogg"),this_week:__("This week","groundhogg"),last_week:__("Last week","groundhogg"),this_month:__("This month","groundhogg"),last_month:__("Last month","groundhogg"),this_year:__("This year","groundhogg"),"24_hours":__("In the last 24 hours","groundhogg"),"7_days":__("In the last 7 days","groundhogg"),"14_days":__("In the last 14 days","groundhogg"),"30_days":__("In the last 30 days","groundhogg"),"60_days":__("In the last 60 days","groundhogg"),"90_days":__("In the last 90 days","groundhogg"),"365_days":__("In the last 365 days","groundhogg"),x_days:__("In the last X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const futureDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),tomorrow:__("Tomorrow","groundhogg"),this_week:__("This week","groundhogg"),this_month:__("This month","groundhogg"),this_year:__("This year","groundhogg"),next_24_hours:__("In the next 24 hours","groundhogg"),next_7_days:__("In the next 7 days","groundhogg"),next_14_days:__("In the next 14 days","groundhogg"),next_30_days:__("In the next 30 days","groundhogg"),next_60_days:__("In the next 60 days","groundhogg"),next_90_days:__("In the next 90 days","groundhogg"),next_365_days:__("In the next 365 days","groundhogg"),next_x_days:__("In the next X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const allDateRanges={...pastDateRanges,...futureDateRanges};const activityFilterComparisons={equals:_x("Exactly","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),greater_than:_x("More than","comparison","groundhogg"),less_than_or_equal_to:_x("At most","comparison","groundhogg"),greater_than_or_equal_to:_x("At least","comparison","groundhogg")};const filterCountComparisons={equals:v=>sprintf(_n("%s time","%s times",parseInt(v),"groundhogg"),v),less_than:v=>sprintf(_n("less than %s time","less than %s times",parseInt(v),"groundhogg"),v),less_than_or_equal_to:v=>sprintf(_n("at most %s time","at most %s times",parseInt(v),"groundhogg"),v),greater_than:v=>sprintf(_n("more than %s time","more than %s times",parseInt(v),"groundhogg"),v),greater_than_or_equal_to:v=>sprintf(_n("at least %s time","at least %s times",parseInt(v),"groundhogg"),v)};const moreComparisonTitleGenerators={all_checked:(prefix,options)=>sprintf(__("%2$s is checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),not_checked:(prefix,options)=>sprintf(__("%2$s is not checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_in:(prefix,options)=>sprintf(__("%2$s is selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_not_in:(prefix,options)=>sprintf(__("%2$s is not selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b))))};const createGroup=(id,name)=>({id:id,name:name});const createFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}},defaults={})=>({type:type,name:name,group:group,edit:edit,display:display,preload:preload,defaults:defaults});const createStringFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:StringComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value},true)}),["empty","not_empty"].includes(compare)?null:Input({id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(value)))},preload:preload},{value:"",compare:"equals",...defaults});const createNumberFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"number",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatNumber(value))))},preload:preload},{value:"",compare:"equals",...defaults});const createTimeFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"time",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatTime(value))))},preload:preload},{value:"",compare:"equals",...defaults});const dateFilterFactory=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={},dateRanges={})=>createFilter(type,name,group,{edit:({date_range,compare:compare="is",before,after,updateFilter,days:days=0,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),InputGroup([Select({id:"filter-compare",name:"compare",options:{is:"Is",is_not:"Is not"},selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Select({id:"filter-date-range",name:"date_range",options:dateRanges,selected:date_range,onChange:e=>updateFilter({date_range:e.target.value},true)})]),["after","between","day_of"].includes(date_range)?Input({type:"date",value:after.split(" ")[0],id:"filter-after",onChange:e=>updateFilter({after:e.target.value})}):null,date_range==="before"||date_range==="between"?Input({type:"date",value:before.split(" ")[0],id:"filter-before",onChange:e=>updateFilter({before:e.target.value})}):null,date_range==="x_days"||date_range==="next_x_days"?Input({type:"number",value:days,name:"days",id:"filter-days",onChange:e=>updateFilter({days:parseInt(e.target.value)})}):null]),display:({compare:compare="is",date_range,after,before,days:days=0,...rest})=>{let prefix=display(rest);if(!prefix||prefix.length===0){prefix=bold(name)}if(compare==="is_not"){prefix+=" is not"}switch(date_range){case"between":return ComparisonsTitleGenerators.between(prefix,formatDate(after),formatDate(before));case"after":case"day_of":return ComparisonsTitleGenerators[date_range](prefix,formatDate(after));case"before":return ComparisonsTitleGenerators.before(prefix,formatDate(before));default:return sprintf("%s %s",prefix,dateRanges[date_range??"any"]?.replace("X",days).toLowerCase())}},preload:preload},{...defaults,before:"",after:"",days:0,compare:"is"});const createDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},allDateRanges);const createPastDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},pastDateRanges);const createFutureDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"next_24_hours",...defaults},futureDateRanges);const createSelectFilter=(type,name,group,options)=>createFilter(type,name,group,{display:({value})=>sprintf("%s is %s",bold(name),bold(options[value])),edit:({value,updateFilter})=>Select({id:"filter-select",options:options,selected:value,onChange:e=>updateFilter({value:e.target.value})})},{value:Object.keys(options)[0]});const OptionsPicker=({field,options,updateFilter})=>ItemPicker({id:"filter-options",noneSelected:"Type to search...",selected:options.map(opt=>({id:opt,text:opt})),fetchOptions:async search=>field.options.filter(opt=>opt.match(new RegExp(search,"i"))).map(opt=>({id:opt,text:opt})),onChange:items=>updateFilter({options:items.map(item=>item.id)})});const filterFactory={text:({id,label,group})=>createStringFilter(id,label,group),url:({id,label,group})=>createStringFilter(id,label,group),custom_email:({id,label,group})=>createStringFilter(id,label,group),tel:({id,label,group})=>createStringFilter(id,label,group),textarea:({id,label,group})=>createStringFilter(id,label,group),number:({id,label,group})=>createNumberFilter(id,label,group),date:({id,label,group})=>createDateFilter(id,label,group),datetime:({id,label,group})=>createDateFilter(id,label,group),time:({id,label,group})=>createTimeFilter(id,label,group),radio:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))},{compare:"in",options:[]}),dropdown:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:field.multiple?{all_in:__("Has all selected"),all_not_in:__("Does not have all selected")}:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>{if(ComparisonsTitleGenerators[compare]){return ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))}return moreComparisonTitleGenerators[compare](bold(label),options)}},{compare:field.multiple?"all_in":"in",options:[]}),checkboxes:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{all_checked:__("Is Checked","groundhogg-better-meta"),not_checked:__("Is Not Checked","groundhogg-better-meta")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options:options=[],compare:compare=""})=>moreComparisonTitleGenerators[compare](bold(label),options)},{compare:"all_checked",options:[]})};const FilterRegistry=({groups:groups=[],filters:filters=[]}={})=>({groups:groups.reduce((carr,curr)=>{carr[curr.id]=curr.name;return carr},{}),filters:filters.reduce((filters,filter)=>{filters[filter.type]=filter;return filters},{}),registerGroup(group,name){if(group&&name){this.groups[group]=name}else{this.groups[group.id]=group.name}},registerFilter(filter){this.filters[filter.type]=filter},displayName(filter){let name=this.getFilter(filter).display(filter);if(!name){name=this.getFilter(filter).name}return name},displayFilters(filters){return filters.map(group=>group.map(filter=>{return Div({},this.display(filter)).innerHTML}).join(" and ")).join(" or ")},filterName(filter){return this.getFilter(filter).name},display(filter){return this.getFilter(filter).display(filter)},edit(filter,updateFilter){return this.getFilter(filter).edit({...filter,updateFilter:updateFilter})},getFilter({type}){return this.filters[type]??{}},hasFilter({type}){return type in this.filters},preloadFilter(filter){return this.getFilter(filter).preload(filter)},preloadFilters(filters){const promises=[];filters.forEach(filterGroup=>filterGroup.forEach(filter=>{try{const promise=this.preloadFilter(filter);if(promise){promises.push(promise)}}catch(err){}}));return Promise.all(promises)},registerFromProperties(properties){const{tabs,fields,groups}=properties;Object.values(tabs).forEach(t=>{Object.values(groups).filter(f=>f.tab===t.id).forEach(s=>{let groupId=`${t.id}-${s.id}`;this.registerGroup(groupId,`${t.name}: ${s.name}`);Object.values(fields).filter(f=>f.group===s.id).forEach(f=>{if(f.type in filterFactory){this.registerFilter(filterFactory[f.type]({...f,group:groupId}))}})})})},registerFromConfig({stringColumns:stringColumns={},numberColumns:numberColumns={},dateColumns:dateColumns={},futureDateColumns:futureDateColumns={},pastDateColumns:pastDateColumns={},selectColumns:selectColumns={},name:name="",group:group="table"}){this.registerGroup(group,name);for(let column in stringColumns){this.registerFilter(createStringFilter(column,stringColumns[column],group))}for(let column in numberColumns){this.registerFilter(createNumberFilter(column,numberColumns[column],group))}for(let column in selectColumns){this.registerFilter(createSelectFilter(column,selectColumns[column][0],group,selectColumns[column][1]))}for(let column in dateColumns){this.registerFilter(createDateFilter(column,dateColumns[column],group),{display:()=>bold(name)})}for(let column in futureDateColumns){this.registerFilter(createFutureDateFilter(column,futureDateColumns[column],group),{display:()=>bold(name)})}for(let column in pastDateColumns){this.registerFilter(createPastDateFilter(column,pastDateColumns[column],group),{display:()=>bold(name)})}return this}});const Filters=({id,filterRegistry:filterRegistry=FilterRegistry({}),filters:filters=[],onChange:onChange=filters=>{}})=>{if(!filters){filters=[]}filters.forEach(filterGroup=>filterGroup.forEach(filter=>{if(!filter.id){filter.id=uuid()}}));const morph=()=>{try{morphdom(document.getElementById(id),FiltersEditor())}catch(e){console.log(e)}};const State=Groundhogg.createState({preloaded:false,activeFilter:null});const setState=(newState,doMorph=true)=>{State.set(newState);if(doMorph){morph()}};const FilterBroken=(filter,group,index,err)=>{let message;if(filterRegistry.hasFilter(filter)){message=err instanceof Error?err.message:sprintf(__("This %s filter is corrupted","groundhogg"),bold(filterRegistry.filterName(filter)))}else{message=sprintf(__("This %s filter is not available.","groundhogg"),bold(filter.type))}return Div({id:`filter-${filter.id}`,className:"filter filter-view filter-broken",tabindex:0,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)}},[Span({className:"filter-name text"},message),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>{e.preventDefault();deleteFilter(group,index)}},Dashicon("no-alt"))])};const Filter=(filter,group,index)=>Div({id:`filter-${filter.id}`,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)},className:"filter filter-view",tabindex:0},[Span({className:"filter-name text"},filterRegistry.displayName(filter)),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("no-alt"))]);const EditFilter=(filter,group,index)=>{let tempFilterSettings={...filter};const morphFilter=()=>{try{morphdom(document.getElementById(`filter-${id}-settings`),FilterSettings())}catch(e){}};const updateTempFilterSettings=(newSettings,doMorph=false)=>{tempFilterSettings={...tempFilterSettings,...newSettings};if(doMorph){morphFilter()}};const FilterSettings=()=>Div({id:`filter-${id}-settings`,className:"settings"},filterRegistry.edit(tempFilterSettings,updateTempFilterSettings));return Div({id:`edit-filter-${filter.id}`,className:`filter filter-edit-wrap filter-${filter.type}`,tabindex:0},Div({className:"filter-edit"},[Div({className:"header"},[bold(filterRegistry.filterName(filter)),Button({type:"button",className:"close-edit",onClick:e=>editFilter(null)},Dashicon("no-alt"))]),FilterSettings(),Div({className:"actions"},[Button({type:"button",id:`delete-${group}-${index}`,className:"delete delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("trash")),Button({type:"button",id:`commit-${group}-${index}`,className:"commit commit-filter",onClick:e=>updateFilter(tempFilterSettings,group,index)},Dashicon("yes"))])]))};const FilterGroup=(filters,group)=>Div({id:`group-${id}-${group}`,className:"group"},[...filters.map((filter,index)=>{try{if(State.activeFilter===filter.id){return EditFilter(filter,group,index)}return Filter(filter,group,index)}catch(err){return FilterBroken(filter,group,index,err)}}),Button({type:"button",id:`add-filter-to-${id}-${group}`,className:"add-filter gh-has-tooltip",onClick:e=>{let options=Object.values(filterRegistry.filters);let groups=filterRegistry.groups;searchOptionsWidget({position:"fixed",target:e.currentTarget,options:options,groups:groups,onSelect:option=>{let newFilter={type:option.type,...option.defaults};console.log(newFilter);addFilter(newFilter,group)},filterOption:(option,search)=>{return option.name.match(regexp(search))},renderOption:option=>option.name,noOptions:__("No matching filters...","groundhogg")}).mount()}},[Dashicon(group===0&&!filters.length?"filter":"plus-alt2"),ToolTip(__("Add a filter","groundhogg"),"right")])]);const addFilter=(filter,group)=>{filter={id:uuid(),...filter};if(filters[group]){filters[group].push(filter)}else{filters.push([filter])}editFilter(filter.id)};const deleteFilter=(group,index)=>{filters[group].splice(index,1);if(!filters[group].length){filters.splice(group,1)}editFilter(null);onChange(filters)};const updateFilter=(newFilter,group,index)=>{filters[group][index]={...filters[group][index],...newFilter};editFilter(null);onChange(filters)};const editFilter=id=>{setState({activeFilter:id})};const GroupSeparator=after=>Div({id:`after-${id}-${after}`,className:"or-separator"},Span({className:"or-circle"},_x("Or...","search filters separator","groundhogg")));const FiltersLoading=()=>Div({id:id,className:`search-filters`},Span({id:`${id}-loading`,className:"filters-loading"},Ellipses(__("Loading"))));const FiltersEditor=()=>{if(!State.get("preloaded")){filterRegistry.preloadFilters(filters).finally(()=>setState({preloaded:true}));return FiltersLoading()}const groups=[];filters.forEach((filterGroup,i)=>{groups.push(FilterGroup(filterGroup,i));groups.push(GroupSeparator(i))});return Div({id:id,className:`search-filters-editor`},[...groups,FilterGroup([],filters.length)])};return FiltersEditor()};const FilterDisplay=({filters,filterRegistry})=>{const renderFilters=()=>Span({},filters.map(row=>{return row.map(filter=>{let result=filterRegistry.displayName(filter);return Span({},result).innerHTML}).join(" <i>AND</i> ")}).join(" <br/><i>OR</i> "));try{return renderFilters()}catch(err){}let el=Span({},["Loading..."]);filterRegistry.preloadFilters(filters).finally(r=>{morphdom(el,renderFilters())});return el};Groundhogg.filters.Filters=Filters;Groundhogg.filters.FilterDisplay=FilterDisplay;Groundhogg.filters.FilterRegistry=FilterRegistry;Groundhogg.filters.createFilter=createFilter;Groundhogg.filters.createGroup=createGroup;Groundhogg.filters.createStringFilter=createStringFilter;Groundhogg.filters.createNumberFilter=createNumberFilter;Groundhogg.filters.createTimeFilter=createTimeFilter;Groundhogg.filters.createPastDateFilter=createPastDateFilter;Groundhogg.filters.createFutureDateFilter=createFutureDateFilter;Groundhogg.filters.createDateFilter=createDateFilter;Groundhogg.filters.createSelectFilter=createSelectFilter;Groundhogg.filters.comparisons={ComparisonsTitleGenerators:ComparisonsTitleGenerators,AllComparisons:AllComparisons,StringComparisons:StringComparisons,NumericComparisons:NumericComparisons,pastDateRanges:pastDateRanges,futureDateRanges:futureDateRanges,allDateRanges:allDateRanges,moreComparisonTitleGenerators:moreComparisonTitleGenerators};if(window.GroundhoggTableFilters){const{id:id="",filters:filters=[],...TableFilterConfig}=GroundhoggTableFilters;const TableFilterRegistry=FilterRegistry({});TableFilterRegistry.registerFromConfig(TableFilterConfig);GroundhoggTableFilters.FilterRegistry=TableFilterRegistry;$(()=>{let tableFiltersEl=document.getElementById("table-filters");if(tableFiltersEl){tableFiltersEl.replaceWith(Filters({id:id,filterRegistry:TableFilterRegistry,filters:filters,onChange:filters=>document.querySelector('form.search-form input[name="include_filters"]').value=base64_json_encode(filters)}))}})}})(jQuery);
     1($=>{const{searchOptionsWidget,bold,uuid,regexp,andList,clickedIn,orList}=Groundhogg.element;const{Div,Button,ItemPicker,Fragment,Input,Select,Span,Ellipses,Dashicon,InputGroup,ToolTip}=MakeEl;const{formatNumber,formatTime,formatDate,formatDateTime}=Groundhogg.formatting;const{sprintf,__,_x,_n}=wp.i18n;const{base64_json_encode}=Groundhogg.functions;const AllComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const StringComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const NumericComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg")};const ComparisonsTitleGenerators={equals:(k,v)=>sprintf(_x("%1$s equals %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_equals:(k,v)=>sprintf(_x("%1$s does not equal %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),contains:(k,v)=>sprintf(_x("%1$s contains %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_contains:(k,v)=>sprintf(_x("%1$s does not contain %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),starts_with:(k,v)=>sprintf(_x("%1$s starts with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),ends_with:(k,v)=>sprintf(_x("%1$s ends with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_start_with:(k,v)=>sprintf(_x("%1$s does not start with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_end_with:(k,v)=>sprintf(_x("%1$s does not end with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than:(k,v)=>sprintf(_x("%1$s is less than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is less than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than:(k,v)=>sprintf(_x("%1$s is greater than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is greater than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),in:(k,v)=>sprintf(_x("%1$s is %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_in:(k,v)=>sprintf(_x("%1$s is not %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),empty:(k,v)=>sprintf(_x("%1$s is empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_empty:(k,v)=>sprintf(_x("%1$s is not empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),includes:(k,v)=>sprintf(_x("%1$s includes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),excludes:(k,v)=>sprintf(_x("%1$s excludes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),before:(k,v)=>sprintf(_x("%1$s is before %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),day_of:(k,v)=>sprintf(_x("%1$s on %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),after:(k,v)=>sprintf(_x("%1$s is after %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),between:(k,v,v2)=>sprintf(_x("%1$s is between %2$s and %3$s","%1 is a key and %2 and %3 are user defined values","groundhogg"),k,v,v2)};const pastDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),yesterday:__("Yesterday","groundhogg"),this_week:__("This week","groundhogg"),last_week:__("Last week","groundhogg"),this_month:__("This month","groundhogg"),last_month:__("Last month","groundhogg"),this_quarter:__("This quarter","groundhogg"),last_quarter:__("Last quarter","groundhogg"),this_year:__("This year","groundhogg"),last_year:__("Last year","groundhogg"),"24_hours":__("In the last 24 hours","groundhogg"),"7_days":__("In the last 7 days","groundhogg"),"14_days":__("In the last 14 days","groundhogg"),"30_days":__("In the last 30 days","groundhogg"),"60_days":__("In the last 60 days","groundhogg"),"90_days":__("In the last 90 days","groundhogg"),"365_days":__("In the last 365 days","groundhogg"),x_days:__("In the last X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const futureDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),tomorrow:__("Tomorrow","groundhogg"),this_week:__("This week","groundhogg"),next_week:__("Next week","groundhogg"),this_month:__("This month","groundhogg"),next_month:__("Next month","groundhogg"),this_quarter:__("This quarter","groundhogg"),next_quarter:__("Next quarter","groundhogg"),this_year:__("This year","groundhogg"),next_year:__("Next year","groundhogg"),next_24_hours:__("In the next 24 hours","groundhogg"),next_7_days:__("In the next 7 days","groundhogg"),next_14_days:__("In the next 14 days","groundhogg"),next_30_days:__("In the next 30 days","groundhogg"),next_60_days:__("In the next 60 days","groundhogg"),next_90_days:__("In the next 90 days","groundhogg"),next_365_days:__("In the next 365 days","groundhogg"),next_x_days:__("In the next X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const allDateRanges={...pastDateRanges,...futureDateRanges};const activityFilterComparisons={equals:_x("Exactly","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),greater_than:_x("More than","comparison","groundhogg"),less_than_or_equal_to:_x("At most","comparison","groundhogg"),greater_than_or_equal_to:_x("At least","comparison","groundhogg")};const filterCountComparisons={equals:v=>sprintf(_n("%s time","%s times",parseInt(v),"groundhogg"),v),less_than:v=>sprintf(_n("less than %s time","less than %s times",parseInt(v),"groundhogg"),v),less_than_or_equal_to:v=>sprintf(_n("at most %s time","at most %s times",parseInt(v),"groundhogg"),v),greater_than:v=>sprintf(_n("more than %s time","more than %s times",parseInt(v),"groundhogg"),v),greater_than_or_equal_to:v=>sprintf(_n("at least %s time","at least %s times",parseInt(v),"groundhogg"),v)};const moreComparisonTitleGenerators={all_checked:(prefix,options)=>sprintf(__("%2$s is checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),not_checked:(prefix,options)=>sprintf(__("%2$s is not checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_in:(prefix,options)=>sprintf(__("%2$s is selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_not_in:(prefix,options)=>sprintf(__("%2$s is not selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b))))};const createGroup=(id,name)=>({id:id,name:name});const createFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}},defaults={})=>({type:type,name:name,group:group,edit:edit,display:display,preload:preload,defaults:defaults});const createStringFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:StringComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value},true)}),["empty","not_empty"].includes(compare)?null:Input({id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(value)))},preload:preload},{value:"",compare:"equals",...defaults});const createNumberFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"number",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatNumber(value))))},preload:preload},{value:"",compare:"equals",...defaults});const createTimeFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"time",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatTime(value))))},preload:preload},{value:"",compare:"equals",...defaults});const dateFilterFactory=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={},dateRanges={})=>createFilter(type,name,group,{edit:({date_range,compare="is",before,after,updateFilter,days=0,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),InputGroup([Select({id:"filter-compare",name:"compare",options:{is:"Is",is_not:"Is not"},selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Select({id:"filter-date-range",name:"date_range",options:dateRanges,selected:date_range,onChange:e=>updateFilter({date_range:e.target.value},true)})]),["after","between","day_of"].includes(date_range)?Input({type:"date",value:after.split(" ")[0],id:"filter-after",onChange:e=>updateFilter({after:e.target.value})}):null,date_range==="before"||date_range==="between"?Input({type:"date",value:before.split(" ")[0],id:"filter-before",onChange:e=>updateFilter({before:e.target.value})}):null,date_range==="x_days"||date_range==="next_x_days"?Input({type:"number",value:days,name:"days",id:"filter-days",onChange:e=>updateFilter({days:parseInt(e.target.value)})}):null]),display:({compare="is",date_range,after,before,days=0,...rest})=>{let prefix=display(rest);if(!prefix||prefix.length===0){prefix=bold(name)}if(compare==="is_not"){prefix+=" is not"}switch(date_range){case"between":return ComparisonsTitleGenerators.between(prefix,formatDate(after),formatDate(before));case"after":case"day_of":return ComparisonsTitleGenerators[date_range](prefix,formatDate(after));case"before":return ComparisonsTitleGenerators.before(prefix,formatDate(before));default:return sprintf("%s %s",prefix,dateRanges[date_range??"any"]?.replace("X",days).toLowerCase())}},preload:preload},{...defaults,before:"",after:"",days:0,compare:"is"});const createDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},allDateRanges);const createPastDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},pastDateRanges);const createFutureDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"next_24_hours",...defaults},futureDateRanges);const createSelectFilter=(type,name,group,options)=>createFilter(type,name,group,{display:({value})=>sprintf("%s is %s",bold(name),bold(options[value])),edit:({value,updateFilter})=>Select({id:"filter-select",options:options,selected:value,onChange:e=>updateFilter({value:e.target.value})})},{value:Object.keys(options)[0]});const OptionsPicker=({field,options,updateFilter})=>ItemPicker({id:"filter-options",noneSelected:"Type to search...",selected:options.map(opt=>({id:opt,text:opt})),fetchOptions:async search=>field.options.filter(opt=>opt.match(new RegExp(search,"i"))).map(opt=>({id:opt,text:opt})),onChange:items=>updateFilter({options:items.map(item=>item.id)})});const filterFactory={text:({id,label,group})=>createStringFilter(id,label,group),url:({id,label,group})=>createStringFilter(id,label,group),custom_email:({id,label,group})=>createStringFilter(id,label,group),tel:({id,label,group})=>createStringFilter(id,label,group),textarea:({id,label,group})=>createStringFilter(id,label,group),number:({id,label,group})=>createNumberFilter(id,label,group),date:({id,label,group})=>createDateFilter(id,label,group),datetime:({id,label,group})=>createDateFilter(id,label,group),time:({id,label,group})=>createTimeFilter(id,label,group),radio:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))},{compare:"in",options:[]}),dropdown:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:field.multiple?{all_in:__("Has all selected"),all_not_in:__("Does not have all selected")}:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>{if(ComparisonsTitleGenerators[compare]){return ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))}return moreComparisonTitleGenerators[compare](bold(label),options)}},{compare:field.multiple?"all_in":"in",options:[]}),checkboxes:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{all_checked:__("Is Checked","groundhogg-better-meta"),not_checked:__("Is Not Checked","groundhogg-better-meta")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options=[],compare=""})=>moreComparisonTitleGenerators[compare](bold(label),options)},{compare:"all_checked",options:[]})};const FilterRegistry=({groups=[],filters=[]}={})=>({groups:groups.reduce((carr,curr)=>{carr[curr.id]=curr.name;return carr},{}),filters:filters.reduce((filters,filter)=>{filters[filter.type]=filter;return filters},{}),registerGroup(group,name){if(group&&name){this.groups[group]=name}else{this.groups[group.id]=group.name}},registerFilter(filter){this.filters[filter.type]=filter},displayName(filter){let name=this.getFilter(filter).display(filter);if(!name){name=this.getFilter(filter).name}return name},displayFilters(filters){return filters.map(group=>group.map(filter=>{return Div({},this.display(filter)).innerHTML}).join(" and ")).join(" or ")},filterName(filter){return this.getFilter(filter).name},display(filter){return this.getFilter(filter).display(filter)},edit(filter,updateFilter){return this.getFilter(filter).edit({...filter,updateFilter:updateFilter})},getFilter({type}){return this.filters[type]??{}},hasFilter({type}){return type in this.filters},preloadFilter(filter){return this.getFilter(filter).preload(filter)},preloadFilters(filters){const promises=[];filters.forEach(filterGroup=>filterGroup.forEach(filter=>{try{const promise=this.preloadFilter(filter);if(promise){promises.push(promise)}}catch(err){}}));return Promise.all(promises)},registerFromProperties(properties){const{tabs,fields,groups}=properties;Object.values(tabs).forEach(t=>{Object.values(groups).filter(f=>f.tab===t.id).forEach(s=>{let groupId=`${t.id}-${s.id}`;this.registerGroup(groupId,`${t.name}: ${s.name}`);Object.values(fields).filter(f=>f.group===s.id).forEach(f=>{if(f.type in filterFactory){this.registerFilter(filterFactory[f.type]({...f,group:groupId}))}})})})},registerFromConfig({stringColumns={},numberColumns={},dateColumns={},futureDateColumns={},pastDateColumns={},selectColumns={},name="",group="table"}){this.registerGroup(group,name);for(let column in stringColumns){this.registerFilter(createStringFilter(column,stringColumns[column],group))}for(let column in numberColumns){this.registerFilter(createNumberFilter(column,numberColumns[column],group))}for(let column in selectColumns){this.registerFilter(createSelectFilter(column,selectColumns[column][0],group,selectColumns[column][1]))}for(let column in dateColumns){this.registerFilter(createDateFilter(column,dateColumns[column],group),{display:()=>bold(name)})}for(let column in futureDateColumns){this.registerFilter(createFutureDateFilter(column,futureDateColumns[column],group),{display:()=>bold(name)})}for(let column in pastDateColumns){this.registerFilter(createPastDateFilter(column,pastDateColumns[column],group),{display:()=>bold(name)})}return this}});const Filters=({id,filterRegistry=FilterRegistry({}),filters=[],onChange=filters=>{}})=>{if(!filters){filters=[]}filters.forEach(filterGroup=>filterGroup.forEach(filter=>{if(!filter.id){filter.id=uuid()}}));const morph=()=>{try{morphdom(document.getElementById(id),FiltersEditor())}catch(e){console.log(e)}};const State=Groundhogg.createState({preloaded:false,activeFilter:null});const setState=(newState,doMorph=true)=>{State.set(newState);if(doMorph){morph()}};const FilterBroken=(filter,group,index,err)=>{let message;if(filterRegistry.hasFilter(filter)){message=err instanceof Error?err.message:sprintf(__("This %s filter is corrupted","groundhogg"),bold(filterRegistry.filterName(filter)))}else{message=sprintf(__("This %s filter is not available.","groundhogg"),bold(filter.type))}return Div({id:`filter-${filter.id}`,className:"filter filter-view filter-broken",tabindex:0,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)}},[Span({className:"filter-name text"},message),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>{e.preventDefault();deleteFilter(group,index)}},Dashicon("no-alt"))])};const Filter=(filter,group,index)=>Div({id:`filter-${filter.id}`,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)},className:"filter filter-view",tabindex:0},[Span({className:"filter-name text"},filterRegistry.displayName(filter)),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("no-alt"))]);const EditFilter=(filter,group,index)=>{let tempFilterSettings={...filter};const morphFilter=()=>{try{morphdom(document.getElementById(`filter-${id}-settings`),FilterSettings())}catch(e){}};const updateTempFilterSettings=(newSettings,doMorph=false)=>{tempFilterSettings={...tempFilterSettings,...newSettings};if(doMorph){morphFilter()}};const FilterSettings=()=>Div({id:`filter-${id}-settings`,className:"settings"},filterRegistry.edit(tempFilterSettings,updateTempFilterSettings));return Div({id:`edit-filter-${filter.id}`,className:`filter filter-edit-wrap filter-${filter.type}`,tabindex:0},Div({className:"filter-edit"},[Div({className:"header"},[bold(filterRegistry.filterName(filter)),Button({type:"button",className:"close-edit",onClick:e=>editFilter(null)},Dashicon("no-alt"))]),FilterSettings(),Div({className:"actions"},[Button({type:"button",id:`delete-${group}-${index}`,className:"delete delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("trash")),Button({type:"button",id:`commit-${group}-${index}`,className:"commit commit-filter",onClick:e=>updateFilter(tempFilterSettings,group,index)},Dashicon("yes"))])]))};const FilterGroup=(filters,group)=>Div({id:`group-${id}-${group}`,className:"group"},[...filters.map((filter,index)=>{try{if(State.activeFilter===filter.id){return EditFilter(filter,group,index)}return Filter(filter,group,index)}catch(err){return FilterBroken(filter,group,index,err)}}),Button({type:"button",id:`add-filter-to-${id}-${group}`,className:"add-filter gh-has-tooltip",onClick:e=>{let options=Object.values(filterRegistry.filters);let groups=filterRegistry.groups;searchOptionsWidget({position:"fixed",target:e.currentTarget,options:options,groups:groups,onSelect:option=>{let newFilter={type:option.type,...option.defaults};console.log(newFilter);addFilter(newFilter,group)},filterOption:(option,search)=>{return option.name.match(regexp(search))},renderOption:option=>option.name,noOptions:__("No matching filters...","groundhogg")}).mount()}},[Dashicon(group===0&&!filters.length?"filter":"plus-alt2"),ToolTip(__("Add a filter","groundhogg"),"right")])]);const addFilter=(filter,group)=>{filter={id:uuid(),...filter};if(filters[group]){filters[group].push(filter)}else{filters.push([filter])}editFilter(filter.id)};const deleteFilter=(group,index)=>{filters[group].splice(index,1);if(!filters[group].length){filters.splice(group,1)}editFilter(null);onChange(filters)};const updateFilter=(newFilter,group,index)=>{filters[group][index]={...filters[group][index],...newFilter};editFilter(null);onChange(filters)};const editFilter=id=>{setState({activeFilter:id})};const GroupSeparator=after=>Div({id:`after-${id}-${after}`,className:"or-separator"},Span({className:"or-circle"},_x("Or...","search filters separator","groundhogg")));const FiltersLoading=()=>Div({id:id,className:`search-filters`},Span({id:`${id}-loading`,className:"filters-loading"},Ellipses(__("Loading"))));const FiltersEditor=()=>{if(!State.get("preloaded")){filterRegistry.preloadFilters(filters).finally(()=>setState({preloaded:true}));return FiltersLoading()}const groups=[];filters.forEach((filterGroup,i)=>{groups.push(FilterGroup(filterGroup,i));groups.push(GroupSeparator(i))});return Div({id:id,className:`search-filters-editor`},[...groups,FilterGroup([],filters.length)])};return FiltersEditor()};const FilterDisplay=({filters,filterRegistry})=>{const renderFilters=()=>Span({},filters.map(row=>{return row.map(filter=>{let result=filterRegistry.displayName(filter);return Span({},result).innerHTML}).join(" <i>AND</i> ")}).join(" <br/><i>OR</i> "));try{return renderFilters()}catch(err){}let el=Span({},["Loading..."]);filterRegistry.preloadFilters(filters).finally(r=>{morphdom(el,renderFilters())});return el};Groundhogg.filters.Filters=Filters;Groundhogg.filters.FilterDisplay=FilterDisplay;Groundhogg.filters.FilterRegistry=FilterRegistry;Groundhogg.filters.createFilter=createFilter;Groundhogg.filters.createGroup=createGroup;Groundhogg.filters.createStringFilter=createStringFilter;Groundhogg.filters.createNumberFilter=createNumberFilter;Groundhogg.filters.createTimeFilter=createTimeFilter;Groundhogg.filters.createPastDateFilter=createPastDateFilter;Groundhogg.filters.createFutureDateFilter=createFutureDateFilter;Groundhogg.filters.createDateFilter=createDateFilter;Groundhogg.filters.createSelectFilter=createSelectFilter;Groundhogg.filters.comparisons={ComparisonsTitleGenerators:ComparisonsTitleGenerators,AllComparisons:AllComparisons,StringComparisons:StringComparisons,NumericComparisons:NumericComparisons,pastDateRanges:pastDateRanges,futureDateRanges:futureDateRanges,allDateRanges:allDateRanges,moreComparisonTitleGenerators:moreComparisonTitleGenerators};if(window.GroundhoggTableFilters){const{id="",filters=[],...TableFilterConfig}=GroundhoggTableFilters;const TableFilterRegistry=FilterRegistry({});TableFilterRegistry.registerFromConfig(TableFilterConfig);GroundhoggTableFilters.FilterRegistry=TableFilterRegistry;$(()=>{let tableFiltersEl=document.getElementById("table-filters");if(tableFiltersEl){tableFiltersEl.replaceWith(Filters({id:id,filterRegistry:TableFilterRegistry,filters:filters,onChange:filters=>document.querySelector('form.search-form input[name="include_filters"]').value=base64_json_encode(filters)}))}})}})(jQuery);
  • groundhogg/tags/4.3.1/assets/js/admin/funnels/simulator.js

    r3269144 r3476690  
    223223          ToolTip('Change contact', 'top'),
    224224        ]),
     225        Button({
     226          id       : `view-profile-${ item.ID }`,
     227          className: 'gh-button secondary text icon',
     228          onClick  : ()=>{
     229            window.open( Groundhogg.stores.contacts.get(State.contactId).admin, '_blank' )
     230          },
     231        }, [
     232          Dashicon('external'),
     233          ToolTip('View profile', 'top'),
     234        ]),
    225235      ]),
    226236    }) : Button({
  • groundhogg/tags/4.3.1/assets/js/admin/funnels/simulator.min.js

    r3269144 r3476690  
    1 (()=>{const{Pg,Div,ItemPicker,Button,Span,Toggle,Label,Dashicon,ToolTip,ProgressBar}=MakeEl;const{icons}=Groundhogg.element;const{ajax}=Groundhogg.api;const{__,sprintf,_x,_n}=wp.i18n;const State=Groundhogg.createState({flow:[],options:[],index:0,current:null,simulating:false,scrollLog:true,contactId:null,dry:true});let contactId=localStorage.getItem("gh-simulate-contact-id");if(contactId){State.set({contactId:parseInt(contactId)})}else{Groundhogg.stores.contacts.fetchItems({users_include:[Groundhogg.user.getCurrentUser().ID]}).then(items=>{if(items.length){State.set({contactId:items[0].ID})}})}const startTimeline=()=>{const interval=setInterval(()=>{if(State.index>=State.flow.length){clearInterval(interval);State.set({simulating:false});Groundhogg.stores.contacts.fetchItem(State.contactId).then(morph);morph();return}if(Number.isInteger(State.flow[State.index])){focusStep(State.flow[State.index])}State.set({index:State.index+1});morph()},500)};const simulate=()=>{State.set({simulating:true});morph();ajax({action:"gh_flow_simulate",from:State.current,contact:State.contactId,dry:State.dry}).then(r=>{State.set({flow:[...State.flow,...r.flow],options:r.options});startTimeline()}).catch(err=>{Groundhogg.element.errorDialog({message:err.message})})};const SimStep=step=>{return Div({id:`flow-log-${step.ID}`,className:["flow-log-item",step.data.step_group,step.data.step_type].join(" "),onClick:e=>{focusStep(step.ID)}},[Div({className:"display-flex gap-10"},[Groundhogg.rawStepTypes[step.data.step_type].svg,Div({className:"display-flex column"},[Span({className:"step-title"},StepTitle(step)),Span({className:"step-name"},Groundhogg.rawStepTypes[step.data.step_type].name)])])])};const StepTitle=step=>{return Span({className:"step-title"},document.querySelector(`#step-${step.ID} .step-title`).innerHTML)};const ContinueStep=step=>Div({id:`flow-sim-continue-from-${step.ID}`,className:"flow-continue-item",onClick:e=>{State.set({current:step.ID});State.flow.push("Trigger selected...");simulate()}},SimStep(step));const focusStep=id=>document.getElementById(`step-${id}`).scrollIntoView({behavior:"smooth",block:"center",inline:"center"});const stepToOpt=step=>{return{id:step.ID,text:Span({className:`gh-text ${step.data.step_group}`},step.data.step_title)}};const handleChangeContact=()=>Groundhogg.components.selectContactModal({onSelect:contact=>{localStorage.setItem("gh-simulate-contact-id",contact.ID);State.set({contactId:contact.ID});morph()}});const ContactPreview=()=>Div({className:"gh-panel outlined"},[State.contactId?Groundhogg.components.ContactListItem(Groundhogg.stores.contacts.get(State.contactId),{extra:item=>Div({className:"display-flex flex-end"},[Button({id:`contact-edit-${item.ID}`,className:"gh-button secondary text icon",onClick:e=>{Groundhogg.components.quickEditContactModal({contact:item,onEdit:item=>{Groundhogg.element.dialog({message:__("Contact updated!","groundhogg")});morph()}})}},[Dashicon("edit"),ToolTip("Quick edit","top")]),Button({id:`contact-swap-${item.ID}`,className:"gh-button secondary text icon",onClick:handleChangeContact},[Dashicon("id"),ToolTip("Change contact","top")])])}):Button({id:"select-contact-for-simulator",onClick:handleChangeContact},"Select a contact")]);const FlowSimulator=()=>{if(State.contactId&&!Groundhogg.stores.contacts.has(State.contactId)){Groundhogg.stores.contacts.maybeFetchItem(State.contactId).then(morph);return Div({id:"flow-simulator"},[Div({className:"skeleton-loading"})])}return Div({id:"flow-simulator"},[Div({className:"start-from display-flex column gap-10"},[ContactPreview(),Div({className:"display-flex gap-5"},[Toggle({id:"simulate-dry-run",checked:State.dry,onChange:e=>State.set({dry:e.target.checked})}),Label({for:"simulate-dry-run"},"Dry run")]),Div({className:"display-flex gap-10"},[ItemPicker({id:"simulate-select-step",noneSelected:"Select a step to start from...",multiple:false,selected:State.current?[stepToOpt(Funnel.getStep(State.current))]:[],fetchOptions:async search=>{let regex=new RegExp(search,"i");return Funnel.steps.filter(item=>item.data.step_title.match(regex)).map(stepToOpt)},onChange:item=>{State.set({current:item.id});focusStep(State.current)}}),Button({id:"run-simulator",className:"gh-button primary",disabled:State.simulating||!State.contactId,onClick:e=>{State.set({index:0,flow:[],options:[]});simulate()}},State.simulating?Span({className:"gh-spinner"}):"Run Simulation")])]),Div({id:"flow-simulator-log",onScroll:e=>{State.set({scrollLog:isAtBottom(e.target)})}},[...State.flow.slice(0,State.index).map(item=>{if(Number.isInteger(item)){let step=Funnel.getStep(item);if(step){return SimStep(Funnel.getStep(item))}}return Pg({className:"flow-log-item"},item)}),State.index>=State.flow.length&&State.options.length&&State.flow.length?Div({},[Pg({},"Select a trigger to continue..."),Div({className:"display-flex gap-20"},[...State.options.map(id=>ContinueStep(Funnel.getStep(id)))])]):null])])};function isAtBottom(el){return el.scrollTop+el.clientHeight>=el.scrollHeight-1}const morph=()=>{morphdom(document.querySelector("#flow-simulator"),FlowSimulator(),{onNodeAdded:el=>{if(el.matches&&el.matches(".flow-log-item")&&State.simulating&&State.scrollLog){scrollLogToBottom()}}})};const scrollLogToBottom=()=>{let log=document.getElementById("flow-simulator-log");log.scrollTo({top:log.scrollHeight,behavior:"smooth"})};document.addEventListener("DOMContentLoaded",morph);Groundhogg.simulator={state:State,FlowSimulator:FlowSimulator,morph:morph}})();
     1(()=>{const{Pg,Div,ItemPicker,Button,Span,Toggle,Label,Dashicon,ToolTip,ProgressBar}=MakeEl;const{icons}=Groundhogg.element;const{ajax}=Groundhogg.api;const{__,sprintf,_x,_n}=wp.i18n;const State=Groundhogg.createState({flow:[],options:[],index:0,current:null,simulating:false,scrollLog:true,contactId:null,dry:true});let contactId=localStorage.getItem("gh-simulate-contact-id");if(contactId){State.set({contactId:parseInt(contactId)})}else{Groundhogg.stores.contacts.fetchItems({users_include:[Groundhogg.user.getCurrentUser().ID]}).then(items=>{if(items.length){State.set({contactId:items[0].ID})}})}const startTimeline=()=>{const interval=setInterval(()=>{if(State.index>=State.flow.length){clearInterval(interval);State.set({simulating:false});Groundhogg.stores.contacts.fetchItem(State.contactId).then(morph);morph();return}if(Number.isInteger(State.flow[State.index])){focusStep(State.flow[State.index])}State.set({index:State.index+1});morph()},500)};const simulate=()=>{State.set({simulating:true});morph();ajax({action:"gh_flow_simulate",from:State.current,contact:State.contactId,dry:State.dry}).then(r=>{State.set({flow:[...State.flow,...r.flow],options:r.options});startTimeline()}).catch(err=>{Groundhogg.element.errorDialog({message:err.message})})};const SimStep=step=>{return Div({id:`flow-log-${step.ID}`,className:["flow-log-item",step.data.step_group,step.data.step_type].join(" "),onClick:e=>{focusStep(step.ID)}},[Div({className:"display-flex gap-10"},[Groundhogg.rawStepTypes[step.data.step_type].svg,Div({className:"display-flex column"},[Span({className:"step-title"},StepTitle(step)),Span({className:"step-name"},Groundhogg.rawStepTypes[step.data.step_type].name)])])])};const StepTitle=step=>{return Span({className:"step-title"},document.querySelector(`#step-${step.ID} .step-title`).innerHTML)};const ContinueStep=step=>Div({id:`flow-sim-continue-from-${step.ID}`,className:"flow-continue-item",onClick:e=>{State.set({current:step.ID});State.flow.push("Trigger selected...");simulate()}},SimStep(step));const focusStep=id=>document.getElementById(`step-${id}`).scrollIntoView({behavior:"smooth",block:"center",inline:"center"});const stepToOpt=step=>{return{id:step.ID,text:Span({className:`gh-text ${step.data.step_group}`},step.data.step_title)}};const handleChangeContact=()=>Groundhogg.components.selectContactModal({onSelect:contact=>{localStorage.setItem("gh-simulate-contact-id",contact.ID);State.set({contactId:contact.ID});morph()}});const ContactPreview=()=>Div({className:"gh-panel outlined"},[State.contactId?Groundhogg.components.ContactListItem(Groundhogg.stores.contacts.get(State.contactId),{extra:item=>Div({className:"display-flex flex-end"},[Button({id:`contact-edit-${item.ID}`,className:"gh-button secondary text icon",onClick:e=>{Groundhogg.components.quickEditContactModal({contact:item,onEdit:item=>{Groundhogg.element.dialog({message:__("Contact updated!","groundhogg")});morph()}})}},[Dashicon("edit"),ToolTip("Quick edit","top")]),Button({id:`contact-swap-${item.ID}`,className:"gh-button secondary text icon",onClick:handleChangeContact},[Dashicon("id"),ToolTip("Change contact","top")]),Button({id:`view-profile-${item.ID}`,className:"gh-button secondary text icon",onClick:()=>{window.open(Groundhogg.stores.contacts.get(State.contactId).admin,"_blank")}},[Dashicon("external"),ToolTip("View profile","top")])])}):Button({id:"select-contact-for-simulator",onClick:handleChangeContact},"Select a contact")]);const FlowSimulator=()=>{if(State.contactId&&!Groundhogg.stores.contacts.has(State.contactId)){Groundhogg.stores.contacts.maybeFetchItem(State.contactId).then(morph);return Div({id:"flow-simulator"},[Div({className:"skeleton-loading"})])}return Div({id:"flow-simulator"},[Div({className:"start-from display-flex column gap-10"},[ContactPreview(),Div({className:"display-flex gap-5"},[Toggle({id:"simulate-dry-run",checked:State.dry,onChange:e=>State.set({dry:e.target.checked})}),Label({for:"simulate-dry-run"},"Dry run")]),Div({className:"display-flex gap-10"},[ItemPicker({id:"simulate-select-step",noneSelected:"Select a step to start from...",multiple:false,selected:State.current?[stepToOpt(Funnel.getStep(State.current))]:[],fetchOptions:async search=>{let regex=new RegExp(search,"i");return Funnel.steps.filter(item=>item.data.step_title.match(regex)).map(stepToOpt)},onChange:item=>{State.set({current:item.id});focusStep(State.current)}}),Button({id:"run-simulator",className:"gh-button primary",disabled:State.simulating||!State.contactId,onClick:e=>{State.set({index:0,flow:[],options:[]});simulate()}},State.simulating?Span({className:"gh-spinner"}):"Run Simulation")])]),Div({id:"flow-simulator-log",onScroll:e=>{State.set({scrollLog:isAtBottom(e.target)})}},[...State.flow.slice(0,State.index).map(item=>{if(Number.isInteger(item)){let step=Funnel.getStep(item);if(step){return SimStep(Funnel.getStep(item))}}return Pg({className:"flow-log-item"},item)}),State.index>=State.flow.length&&State.options.length&&State.flow.length?Div({},[Pg({},"Select a trigger to continue..."),Div({className:"display-flex gap-20"},[...State.options.map(id=>ContinueStep(Funnel.getStep(id)))])]):null])])};function isAtBottom(el){return el.scrollTop+el.clientHeight>=el.scrollHeight-1}const morph=()=>{morphdom(document.querySelector("#flow-simulator"),FlowSimulator(),{onNodeAdded:el=>{if(el.matches&&el.matches(".flow-log-item")&&State.simulating&&State.scrollLog){scrollLogToBottom()}}})};const scrollLogToBottom=()=>{let log=document.getElementById("flow-simulator-log");log.scrollTo({top:log.scrollHeight,behavior:"smooth"})};document.addEventListener("DOMContentLoaded",morph);Groundhogg.simulator={state:State,FlowSimulator:FlowSimulator,morph:morph}})();
  • groundhogg/tags/4.3.1/assets/js/admin/reports/reporting.js

    r3264477 r3476690  
    247247        }))
    248248
     249      },
     250
     251      setOtherDataAndRefresh: function (data) {
     252        this.other = {
     253          ...this.other,
     254          ...data,
     255        }
     256        this.refresh(this.calendar)
    249257      },
    250258
  • groundhogg/tags/4.3.1/assets/js/admin/reports/reporting.min.js

    r3264477 r3476690  
    1 (function($,nonces){const{sprintf,__,_x,_n}=wp.i18n;const{emails:EmailsStore,campaigns:CampaignsStore}=Groundhogg.stores;const{ItemPicker,Table,Tr,Td,Th,THead,TBody,Fragment,Button,Div,Span}=MakeEl;const{loadingModal,adminPageURL}=Groundhogg.element;const{base64_json_encode}=Groundhogg.functions;const openInContactsView=filters=>{window.open(adminPageURL("gh_contacts",{filters:base64_json_encode(filters)}),"_blank")};const ReportTable=(id,report)=>{let{label,data,no_data:no_data="",per_page:per_page=10,orderby:orderby=0}=report;if(!Array.isArray(label)){label=Object.values(label)}let sortable=data.length&&data[0].orderby;const State=Groundhogg.createState({per_page:per_page,orderby:orderby,orderby2:0,order:"DESC",page:0});const compareRows=(a,b,k=State.orderby)=>{if(!sortable||!a.orderby){return 0}let av=a.orderby[k];let bv=b.orderby[k];if(av===bv&&k!==State.orderby2){return compareRows(a,b,State.orderby2)}if(State.order==="ASC"){return av-bv}return bv-av};const getData=()=>data.sort(compareRows).slice(State.per_page*State.page,State.per_page*State.page+State.per_page);const TableBody=()=>TBody({},getData().map(({orderby:orderby={},cellClasses:cellClasses=[],...row})=>Tr({},Object.keys(row).map((k,i)=>{return Td({dataColname:k,className:`${cellClasses[i]??""}`},`${row[k]}`)}))));return Div({id:`report-${id}`},morph=>Fragment([Div({className:"table-scroll"},Table({className:"groundhogg-report-table"},[THead({},Tr({},label.map((l,i)=>Th({id:`order-${i}`,className:`${State.orderby===i||State.orderby2===i?"sorted":""} ${State.order==="ASC"?"asc":"desc"}`,onClick:e=>{if(!sortable){return}if(State.orderby===i){State.set({order:State.order==="ASC"?"DESC":"ASC"})}else{State.set({orderby:i,orderby2:State.orderby,order:"DESC"})}morph()}},Div({className:`display-flex ${i===0?"flex-start":i===label.length-1?"flex-end":"center"}`},[Span({className:"column-name"},l),sortable?Span({},[Span({className:"sorting-indicator asc"}),Span({className:"sorting-indicator desc"})]):null]))))),TableBody()])),data.length>State.per_page?Div({style:{padding:"10px"},className:"display-flex gap-10 flex-end"},[State.page>0?Button({id:`report-${id}-prev`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page-1});morph()}},"Prev"):null,(State.page+1)*State.per_page<data.length?Button({id:`report-${id}-next`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page+1});morph()}},"Next"):null]):null]))};if(typeof GroundhoggReporting!=="undefined"){const reporting=GroundhoggReporting;$.extend(reporting||{},{data:{},calendar:null,charts:{},init:function(){this.initCalendar();this.initFunnels();this.initCountry();this.initBroadcast();this.initCampaignFilter()},async initCampaignFilter(){let el=document.getElementById("report-campaign-filter");if(!el){return}let campaignId=this.other.campaign;if(campaignId&&!CampaignsStore.has(campaignId)){await CampaignsStore.fetchItem(campaignId)}el.append(ItemPicker({id:"report-campaign",noneSelected:__("Filter by campaign...","groundhogg"),multiple:false,selected:campaignId?(({ID,data})=>({id:ID,text:data.name}))(CampaignsStore.get(campaignId)):[],fetchOptions:async search=>{let campaigns=await CampaignsStore.fetchItems({search:search,limit:20});return campaigns.map(({ID,data})=>({id:ID,text:data.name}))},onChange:item=>{if(!item){this.other.campaign=null}else{this.other.campaign=item.id}this.refresh(this.calendar)}}))},initCalendar:function(){var self=this;this.calendar=new Calendar({element:$("#groundhogg-datepicker"),presets:[{label:"Last 30 days",start:moment().subtract(29,"days"),end:moment()},{label:"This month",start:moment().startOf("month"),end:moment().endOf("month")},{label:"Last month",start:moment().subtract(1,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"Last 7 days",start:moment().subtract(6,"days"),end:moment()},{label:"Last 3 months",start:moment().subtract(3,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"This year",start:moment().startOf("year"),end:moment().endOf("year")}],format:{preset:GroundhoggReporting.date_format},earliest_date:"January 1, 2017",latest_date:moment(),start_date:self.dates.start_date,end_date:self.dates.end_date,callback:function(){self.refresh(this)}});this.calendar.calendarSaveDates()},initFunnels:function(){var self=this;$("#funnel-id").change(function(){self.refresh(self.calendar)})},initBroadcast:function(){var self=this;$("#broadcast-id").change(function(){self.refresh(self.calendar)})},initCountry:function(){var self=this;$("#country").change(function(){self.refresh(self.calendar)})},refresh:function(calendar){var self=this;let{close}=loadingModal();var start=calendar.start_date.format("YYYY-MM-DD"),end=calendar.end_date.format("YYYY-MM-DD");$.ajax({type:"post",url:ajaxurl,dataType:"json",data:{action:"groundhogg_refresh_dashboard_reports",reports:self.reports,start:start,end:end,data:{...reporting.other}},success:function(json){self.data=json.data.reports;self.renderReports();close();$(".wrap").removeClass("blurred");window.dispatchEvent(new Event("resize"))},failure:function(response){alert("Unable to retrieve data...")}})},get_other_data:function(){var self=this;var data={};$(".post-data").each(function(i){var $this=$(this);var name=$this.attr("name");if(name==="funnel"){name="funnel_id"}data[name]=$this.val()});return data},renderReports:function(){for(var i=0;i<this.reports.length;i++){var report_id=this.reports[i];var report_data=this.data[report_id];this.renderReport(report_id,report_data)}},renderReport:function(report_id,report_data){var $report=$("#"+report_id);if(!$report.length){return}var type=report_data.type;switch(type){case"quick_stat":this.renderQuickStatReport($report,report_data);break;case"chart":this.renderChartReport($report,report_data.chart,report_id);break;case"table":this.renderTable($report,report_data,report_id);break;case"funnel":this.renderFunnelFlowReport(report_data);break;case"funnel_breakdown":this.renderFunnelBreakdownReport($report,report_data);break}},renderFunnelBreakdownReport($report,report_data){const{report}=report_data;$report.html("");let reportEl=$report[0];let labelsEl=MakeEl.Div({className:"funnel-labels"},[...report.map(({link,labels,percentage:percentage=null})=>{return MakeEl.Fragment([MakeEl.Div({className:"funnel-stage-label"},MakeEl.Span({},Groundhogg.element.orList(labels)))])})]);let minWidth=10;let funnelEl=MakeEl.Div({className:"funnel-stages"},[...report.map(({link,width,complete,percentage:percentage=null})=>{let percentEl=null;if(percentage!==null){percentEl=MakeEl.Div({className:"stage-percentage",style:{width:`${Math.max(width,minWidth)}%`}},percentage==width?`${percentage}%`:`${percentage}% (${width}%)`)}return MakeEl.Fragment([percentEl,MakeEl.Div({className:"funnel-stage-layer",style:{width:`${Math.max(width,minWidth)}%`},onClick:e=>{let a=e.currentTarget.querySelector("a");if(a){a.click()}}},[link])])})]);reportEl.append(labelsEl);reportEl.append(funnelEl);applyClipPath(funnelEl)},renderFunnelFlowReport(report_data){const{stepData:stepData=[]}=report_data;stepData.forEach(({step:step=0,complete:complete="",waiting:waiting=""})=>{step=document.getElementById(`step-${step}`);if(!step){return}let completeEl=step.querySelector(".complete");let waitingEl=step.querySelector(".waiting");if(completeEl){completeEl.innerHTML=complete}if(waitingEl){waitingEl.innerHTML=waiting;if(waiting==="0"){waitingEl.closest(".stat-wrap").classList.add("invisible")}else{waitingEl.closest(".stat-wrap").classList.remove("invisible")}}});Groundhogg.drawLogicLines()},renderQuickStatReport:function($report,report_data){$report.find(".groundhogg-quick-stat-number").html(report_data.number);$report.find(".groundhogg-quick-stat-previous").removeClass("green red").addClass(report_data.compare.arrow.color);$report.find(".groundhogg-quick-stat-compare").html(report_data.compare.text);$report.find(".groundhogg-quick-stat-arrow").removeClass("up down").addClass(report_data.compare.arrow.direction);$report.find(".groundhogg-quick-stat-prev-percent").html(report_data.compare.percent)},renderChartReport:function($report,report_data,report_id){if(report_data.data.labels&&report_data.data.labels.length===0){$report.closest(".gh-donut-chart-wrap").html(report_data.no_data);return}if(this.charts[report_id]){this.charts[report_id].destroy()}var ctx=$report[0].getContext("2d");switch(report_id){case"chart_unsub_reasons":report_data.options.onClick=(e,arr)=>{let index=arr[0]._index;let{reason}=report_data.data.rawResults[index];openInContactsView([[{type:"unsubscribed",reasons:[reason],date_range:"between",before:this.calendar.end_date.format("YYYY-MM-DD"),after:this.calendar.start_date.format("YYYY-MM-DD")}]])};break}var chart=new Chart(ctx,report_data);this.charts[report_id]=chart;var draw_line=Chart.controllers.line.prototype.draw;Chart.helpers.extend(Chart.controllers.line.prototype,{draw:function(){draw_line.apply(this,arguments);if(this.chart.tooltip._active&&this.chart.tooltip._active.length){var ap=this.chart.tooltip._active[0];var ctx=this.chart.ctx;var x=ap.tooltipPosition().x;var topy=this.chart.scales["y-axis-0"].top;var bottomy=this.chart.scales["y-axis-0"].bottom;ctx.save();ctx.beginPath();ctx.moveTo(x,topy);ctx.lineTo(x,bottomy);ctx.lineWidth=1;ctx.strokeStyle="#727272";ctx.setLineDash([10,10]);ctx.stroke();ctx.restore()}}});Chart.plugins.register({afterDraw:function(chart){if(chart.data.datasets.length===0){var ctx=chart.chart.ctx;var width=chart.chart.width;var height=chart.chart.height;chart.clear();ctx.save();ctx.textAlign="center";ctx.textBaseline="middle";ctx.font="16px normal 'Helvetica Nueue'";ctx.fillText("No data to display",width/2,height/2);ctx.restore()}}})},renderTable:function($report,report_data,id){let{data,no_data:no_data=""}=report_data;if(!data.length){$report.html(no_data);return}$report.html(ReportTable(id,report_data))}})}function applyClipPath(element){const boxes=element.children;for(let i=0;i<boxes.length-1;i++){const current=boxes[i];const next=boxes[i+1];const currentWidth=current.offsetWidth;const nextWidth=next.offsetWidth;const leftDiff=(currentWidth-nextWidth)/2;const rightDiff=(currentWidth-nextWidth)/2;current.style.clipPath=`polygon(
     1(function($,nonces){const{sprintf,__,_x,_n}=wp.i18n;const{emails:EmailsStore,campaigns:CampaignsStore}=Groundhogg.stores;const{ItemPicker,Table,Tr,Td,Th,THead,TBody,Fragment,Button,Div,Span}=MakeEl;const{loadingModal,adminPageURL}=Groundhogg.element;const{base64_json_encode}=Groundhogg.functions;const openInContactsView=filters=>{window.open(adminPageURL("gh_contacts",{filters:base64_json_encode(filters)}),"_blank")};const ReportTable=(id,report)=>{let{label,data,no_data="",per_page=10,orderby=0}=report;if(!Array.isArray(label)){label=Object.values(label)}let sortable=data.length&&data[0].orderby;const State=Groundhogg.createState({per_page:per_page,orderby:orderby,orderby2:0,order:"DESC",page:0});const compareRows=(a,b,k=State.orderby)=>{if(!sortable||!a.orderby){return 0}let av=a.orderby[k];let bv=b.orderby[k];if(av===bv&&k!==State.orderby2){return compareRows(a,b,State.orderby2)}if(State.order==="ASC"){return av-bv}return bv-av};const getData=()=>data.sort(compareRows).slice(State.per_page*State.page,State.per_page*State.page+State.per_page);const TableBody=()=>TBody({},getData().map(({orderby={},cellClasses=[],...row})=>Tr({},Object.keys(row).map((k,i)=>{return Td({dataColname:k,className:`${cellClasses[i]??""}`},`${row[k]}`)}))));return Div({id:`report-${id}`},morph=>Fragment([Div({className:"table-scroll"},Table({className:"groundhogg-report-table"},[THead({},Tr({},label.map((l,i)=>Th({id:`order-${i}`,className:`${State.orderby===i||State.orderby2===i?"sorted":""} ${State.order==="ASC"?"asc":"desc"}`,onClick:e=>{if(!sortable){return}if(State.orderby===i){State.set({order:State.order==="ASC"?"DESC":"ASC"})}else{State.set({orderby:i,orderby2:State.orderby,order:"DESC"})}morph()}},Div({className:`display-flex ${i===0?"flex-start":i===label.length-1?"flex-end":"center"}`},[Span({className:"column-name"},l),sortable?Span({},[Span({className:"sorting-indicator asc"}),Span({className:"sorting-indicator desc"})]):null]))))),TableBody()])),data.length>State.per_page?Div({style:{padding:"10px"},className:"display-flex gap-10 flex-end"},[State.page>0?Button({id:`report-${id}-prev`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page-1});morph()}},"Prev"):null,(State.page+1)*State.per_page<data.length?Button({id:`report-${id}-next`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page+1});morph()}},"Next"):null]):null]))};if(typeof GroundhoggReporting!=="undefined"){const reporting=GroundhoggReporting;$.extend(reporting||{},{data:{},calendar:null,charts:{},init:function(){this.initCalendar();this.initFunnels();this.initCountry();this.initBroadcast();this.initCampaignFilter()},async initCampaignFilter(){let el=document.getElementById("report-campaign-filter");if(!el){return}let campaignId=this.other.campaign;if(campaignId&&!CampaignsStore.has(campaignId)){await CampaignsStore.fetchItem(campaignId)}el.append(ItemPicker({id:"report-campaign",noneSelected:__("Filter by campaign...","groundhogg"),multiple:false,selected:campaignId?(({ID,data})=>({id:ID,text:data.name}))(CampaignsStore.get(campaignId)):[],fetchOptions:async search=>{let campaigns=await CampaignsStore.fetchItems({search:search,limit:20});return campaigns.map(({ID,data})=>({id:ID,text:data.name}))},onChange:item=>{if(!item){this.other.campaign=null}else{this.other.campaign=item.id}this.refresh(this.calendar)}}))},setOtherDataAndRefresh:function(data){this.other={...this.other,...data};this.refresh(this.calendar)},initCalendar:function(){var self=this;this.calendar=new Calendar({element:$("#groundhogg-datepicker"),presets:[{label:"Last 30 days",start:moment().subtract(29,"days"),end:moment()},{label:"This month",start:moment().startOf("month"),end:moment().endOf("month")},{label:"Last month",start:moment().subtract(1,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"Last 7 days",start:moment().subtract(6,"days"),end:moment()},{label:"Last 3 months",start:moment().subtract(3,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"This year",start:moment().startOf("year"),end:moment().endOf("year")}],format:{preset:GroundhoggReporting.date_format},earliest_date:"January 1, 2017",latest_date:moment(),start_date:self.dates.start_date,end_date:self.dates.end_date,callback:function(){self.refresh(this)}});this.calendar.calendarSaveDates()},initFunnels:function(){var self=this;$("#funnel-id").change(function(){self.refresh(self.calendar)})},initBroadcast:function(){var self=this;$("#broadcast-id").change(function(){self.refresh(self.calendar)})},initCountry:function(){var self=this;$("#country").change(function(){self.refresh(self.calendar)})},refresh:function(calendar){var self=this;let{close}=loadingModal();var start=calendar.start_date.format("YYYY-MM-DD"),end=calendar.end_date.format("YYYY-MM-DD");$.ajax({type:"post",url:ajaxurl,dataType:"json",data:{action:"groundhogg_refresh_dashboard_reports",reports:self.reports,start:start,end:end,data:{...reporting.other}},success:function(json){self.data=json.data.reports;self.renderReports();close();$(".wrap").removeClass("blurred");window.dispatchEvent(new Event("resize"))},failure:function(response){alert("Unable to retrieve data...")}})},get_other_data:function(){var self=this;var data={};$(".post-data").each(function(i){var $this=$(this);var name=$this.attr("name");if(name==="funnel"){name="funnel_id"}data[name]=$this.val()});return data},renderReports:function(){for(var i=0;i<this.reports.length;i++){var report_id=this.reports[i];var report_data=this.data[report_id];this.renderReport(report_id,report_data)}},renderReport:function(report_id,report_data){var $report=$("#"+report_id);if(!$report.length){return}var type=report_data.type;switch(type){case"quick_stat":this.renderQuickStatReport($report,report_data);break;case"chart":this.renderChartReport($report,report_data.chart,report_id);break;case"table":this.renderTable($report,report_data,report_id);break;case"funnel":this.renderFunnelFlowReport(report_data);break;case"funnel_breakdown":this.renderFunnelBreakdownReport($report,report_data);break}},renderFunnelBreakdownReport($report,report_data){const{report}=report_data;$report.html("");let reportEl=$report[0];let labelsEl=MakeEl.Div({className:"funnel-labels"},[...report.map(({link,labels,percentage=null})=>{return MakeEl.Fragment([MakeEl.Div({className:"funnel-stage-label"},MakeEl.Span({},Groundhogg.element.orList(labels)))])})]);let minWidth=10;let funnelEl=MakeEl.Div({className:"funnel-stages"},[...report.map(({link,width,complete,percentage=null})=>{let percentEl=null;if(percentage!==null){percentEl=MakeEl.Div({className:"stage-percentage",style:{width:`${Math.max(width,minWidth)}%`}},percentage==width?`${percentage}%`:`${percentage}% (${width}%)`)}return MakeEl.Fragment([percentEl,MakeEl.Div({className:"funnel-stage-layer",style:{width:`${Math.max(width,minWidth)}%`},onClick:e=>{let a=e.currentTarget.querySelector("a");if(a){a.click()}}},[link])])})]);reportEl.append(labelsEl);reportEl.append(funnelEl);applyClipPath(funnelEl)},renderFunnelFlowReport(report_data){const{stepData=[]}=report_data;stepData.forEach(({step=0,complete="",waiting=""})=>{step=document.getElementById(`step-${step}`);if(!step){return}let completeEl=step.querySelector(".complete");let waitingEl=step.querySelector(".waiting");if(completeEl){completeEl.innerHTML=complete}if(waitingEl){waitingEl.innerHTML=waiting;if(waiting==="0"){waitingEl.closest(".stat-wrap").classList.add("invisible")}else{waitingEl.closest(".stat-wrap").classList.remove("invisible")}}});Groundhogg.drawLogicLines()},renderQuickStatReport:function($report,report_data){$report.find(".groundhogg-quick-stat-number").html(report_data.number);$report.find(".groundhogg-quick-stat-previous").removeClass("green red").addClass(report_data.compare.arrow.color);$report.find(".groundhogg-quick-stat-compare").html(report_data.compare.text);$report.find(".groundhogg-quick-stat-arrow").removeClass("up down").addClass(report_data.compare.arrow.direction);$report.find(".groundhogg-quick-stat-prev-percent").html(report_data.compare.percent)},renderChartReport:function($report,report_data,report_id){if(report_data.data.labels&&report_data.data.labels.length===0){$report.closest(".gh-donut-chart-wrap").html(report_data.no_data);return}if(this.charts[report_id]){this.charts[report_id].destroy()}var ctx=$report[0].getContext("2d");switch(report_id){case"chart_unsub_reasons":report_data.options.onClick=(e,arr)=>{let index=arr[0]._index;let{reason}=report_data.data.rawResults[index];openInContactsView([[{type:"unsubscribed",reasons:[reason],date_range:"between",before:this.calendar.end_date.format("YYYY-MM-DD"),after:this.calendar.start_date.format("YYYY-MM-DD")}]])};break}var chart=new Chart(ctx,report_data);this.charts[report_id]=chart;var draw_line=Chart.controllers.line.prototype.draw;Chart.helpers.extend(Chart.controllers.line.prototype,{draw:function(){draw_line.apply(this,arguments);if(this.chart.tooltip._active&&this.chart.tooltip._active.length){var ap=this.chart.tooltip._active[0];var ctx=this.chart.ctx;var x=ap.tooltipPosition().x;var topy=this.chart.scales["y-axis-0"].top;var bottomy=this.chart.scales["y-axis-0"].bottom;ctx.save();ctx.beginPath();ctx.moveTo(x,topy);ctx.lineTo(x,bottomy);ctx.lineWidth=1;ctx.strokeStyle="#727272";ctx.setLineDash([10,10]);ctx.stroke();ctx.restore()}}});Chart.plugins.register({afterDraw:function(chart){if(chart.data.datasets.length===0){var ctx=chart.chart.ctx;var width=chart.chart.width;var height=chart.chart.height;chart.clear();ctx.save();ctx.textAlign="center";ctx.textBaseline="middle";ctx.font="16px normal 'Helvetica Nueue'";ctx.fillText("No data to display",width/2,height/2);ctx.restore()}}})},renderTable:function($report,report_data,id){let{data,no_data=""}=report_data;if(!data.length){$report.html(no_data);return}$report.html(ReportTable(id,report_data))}})}function applyClipPath(element){const boxes=element.children;for(let i=0;i<boxes.length-1;i++){const current=boxes[i];const next=boxes[i+1];const currentWidth=current.offsetWidth;const nextWidth=next.offsetWidth;const leftDiff=(currentWidth-nextWidth)/2;const rightDiff=(currentWidth-nextWidth)/2;current.style.clipPath=`polygon(
    22                    0% 0%,
    33                    100% 0%,
  • groundhogg/tags/4.3.1/db/query/filters.php

    r3400645 r3476690  
    179179                $before->modify( 'tomorrow 23:59:59' );
    180180                break;
     181
    181182            case 'this_week':
    182 
    183183                $startEnd = get_weekstartend( $after->ymdhis() );
    184184                $after->setTimestamp( $startEnd['start'] );
    185185                $before->setTimestamp( $startEnd['end'] );
    186 
    187186                break;
    188187            case 'last_week':
    189 
    190188                $after->modify( '7 days ago' );
    191189                $startEnd = get_weekstartend( $after->ymdhis() );
    192190                $after->setTimestamp( $startEnd['start'] );
    193191                $before->setTimestamp( $startEnd['end'] );
    194 
    195                 break;
    196 
     192                break;
     193            case 'next_week':
     194                $after->modify( '+7 days' );
     195                $startEnd = get_weekstartend( $after->ymdhis() );
     196                $after->setTimestamp( $startEnd['start'] );
     197                $before->setTimestamp( $startEnd['end'] );
     198                break;
    197199            case 'this_month':
    198200                $after->modify( 'first day of this month 00:00:00' );
     
    203205                $before->modify( 'last day of last month 23:59:59' );
    204206                break;
     207            case 'next_month':
     208                $after->modify( 'first day of next month 00:00:00' );
     209                $before->modify( 'last day of next month 23:59:59' );
     210                break;
     211            case 'this_quarter':
     212                $after->toStartOfQuarter();
     213                $before->toEndOfQuarter();
     214                break;
     215            case 'last_quarter':
     216                $after->modify('-3 months')->toStartOfQuarter();
     217                $before->modify('-3 months')->toEndOfQuarter();
     218                break;
     219            case 'next_quarter':
     220                $after->modify( '+3 months')->toStartOfQuarter();
     221                $before->modify( '+3 months')->toEndOfQuarter();
     222                break;
    205223            case 'this_year':
    206224                $after->modify( 'first day of January this year 00:00:00' );
    207225                $before->modify( 'last day of December this year 23:59:59' );
     226                break;
     227            case 'last_year':
     228                $after->modify('-1 year')->modify( 'first day of January this year 00:00:00' );
     229                $before->modify('-1 year')->modify( 'last day of December this year 23:59:59' );
     230                break;
     231            case 'next_year':
     232                $after->modify('+1 year')->modify( 'first day of January this year 00:00:00' );
     233                $before->modify('+1 year')->modify( 'last day of December this year 23:59:59' );
    208234                break;
    209235            case '24_hours':
  • groundhogg/tags/4.3.1/groundhogg.php

    r3464589 r3476690  
    44 * Plugin URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash
    55 * Description: CRM and marketing automation for WordPress
    6  * Version: 4.3
     6 * Version: 4.3.1
    77 * Author: Groundhogg Inc.
    88 * Author URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash
     
    2525if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
    2626
    27 define( 'GROUNDHOGG_VERSION', '4.3' );
    28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.12' );
     27define( 'GROUNDHOGG_VERSION', '4.3.1' );
     28define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.3' );
    2929
    3030define( 'GROUNDHOGG__FILE__', __FILE__ );
  • groundhogg/tags/4.3.1/includes/better-meta-compat.php

    r3422142 r3476690  
    6262            return sanitize_textarea_field( $value );
    6363        case 'number':
    64             return intval( $value );
     64            return number_format( (float) $value, $field['decimals'] ?? 0 );
    6565        case 'time':
    6666            return date( 'H:i:s', strtotime( $value ) );
  • groundhogg/tags/4.3.1/includes/functions.php

    r3464589 r3476690  
    33namespace Groundhogg;
    44
     5use Groundhogg\Admin\Funnels\Simulator;
    56use Groundhogg\Classes\Activity;
    67use Groundhogg\Classes\Page_Visit;
     
    89158916 */
    89168917function add_event_args( $args = [] ) {
     8918
     8919    // special handling for the simulator
     8920    if ( Simulator::is_simulating() ){
     8921        Simulator::set_event_args( $args );
     8922        return true;
     8923    }
     8924
    89178925    if ( ! \Groundhogg\event_queue()::is_processing() ) {
    89188926        return false;
     
    89338941 */
    89348942function get_event_arg( string $arg, $default = false ) {
     8943
     8944    // special handling for the simulator
     8945    if ( Simulator::is_simulating() ){
     8946        return Simulator::get_event_arg( $arg, $default );
     8947    }
    89358948
    89368949    if ( ! \Groundhogg\event_queue()::is_processing() ) {
  • groundhogg/tags/4.3.1/includes/properties.php

    r3343709 r3476690  
    9797            'order'   => 'absint',
    9898            'width'   => 'absint',
     99            'decimals' => 'absint',
    99100            'multiple' => 'boolval',
    100101            'options' => function ( $array ) {
  • groundhogg/tags/4.3.1/includes/utils/date-time-helper.php

    r3422142 r3476690  
    116116    public function date_i18n() {
    117117        return $this->i18n( get_option( 'date_format' ) );
     118    }
     119
     120    /**
     121     * Move to the start of the quarter relative to the current time
     122     *
     123     * @return $this
     124     */
     125    public function toStartOfQuarter() {
     126
     127        $month = (int) $this->format('n');
     128        $quarter = (int) ceil($month / 3);
     129
     130        $start = (clone $this)->setDate(
     131            (int) $this->format('Y'),
     132            (($quarter - 1) * 3) + 1,
     133            1
     134        )->setTime(0, 0, 0);
     135
     136        $this->setTimestamp( $start->getTimestamp() );
     137
     138        return $this;
     139    }
     140
     141    /**
     142     * Move to the end of the quarter relative to the current time
     143     *
     144     * @throws \DateMalformedStringException
     145     * @return \DateTime|false
     146     */
     147    public function toEndOfQuarter() {
     148        return $this->toStartOfQuarter()
     149                    ->modify('+3 months')
     150                    ->modify('-1 second');
    118151    }
    119152
  • groundhogg/tags/4.3.1/includes/utils/html.php

    r3344140 r3476690  
    17201720    }
    17211721
     1722    /**
     1723     * An easy tooltip helper
     1724     *
     1725     * @param  string  $text
     1726     * @param  string  $position
     1727     * @param $echo
     1728     *
     1729     * @return string
     1730     */
     1731    public function info_tooltip( string $text, string $position = 'top', $echo = false ) {
     1732        return $this->e( 'span', [ 'class' => 'dashicons dashicons-info' ], [
     1733            $this->e('span', [ 'class' => 'gh-tooltip ' . $position ], $text ),
     1734        ], '', $echo );
     1735    }
     1736
     1737    public function help_tooltip( string $text ) {
     1738
     1739    }
     1740
    17221741}
  • groundhogg/trunk/README.txt

    r3464589 r3476690  
    77Tested up to: 6.9
    88Requires PHP: 7.1
    9 Stable tag: 4.3
     9Stable tag: 4.3.1
    1010License: GPLv3
    1111License URI: https://www.gnu.org/licenses/gpl.md
     
    379379== Changelog ==
    380380
     381= 4.3.1 (2026-03-06) =
     382* ADDED Quarter time ranges (This Quarter, Next Quarter, Last Quarter) for date filters.
     383* ADDED Decimal support for number fields.
     384* ADDED View profile button to the simulator contact card.
     385* FIXED Missing support for event arguments in the simulator (backend magic wizardy).
     386* FIXED Flow template categories and search feature not working if the toolbar widget was disabled.
     387
    381388= 4.3 (2026-02-13) =
    382389* ADDED 🌐 Global blocks (email template parts) in the email editor.
    383  * Save any block as a Global Block
    384  * Are synced across all your templates
    385  * Can be edited inline
    386  * Can be detached to edit separately
    387  * Can be exported and imported
     390 * Save any block as a Global Block.
     391 * Are synced across all your templates.
     392 * Can be edited inline.
     393 * Can be detached to edit separately.
     394 * Can be exported and imported.
    388395* TWEAKED Editor design
    389  * Reduced block toolbar icon sizes
    390  * Added a container border for container blocks in the block inspector
     396 * Reduced block toolbar icon sizes.
     397 * Added a container border for container blocks in the block inspector.
    391398
    392399= 4.2.12 (2026-02-10) =
  • groundhogg/trunk/admin/broadcasts/broadcasts-page.php

    r3394550 r3476690  
    5050        }
    5151
    52         $total_contacts  = get_post_var( 'total_contacts' );
    53         $amount          = get_post_var( 'batch_amount' );
    54         $interval        = get_post_var( 'batch_interval' );
    55         $interval_length = get_post_var( 'batch_interval_length' );
     52        $total_contacts  = absint( get_post_var( 'total_contacts' ) );
     53        $amount          = absint( get_post_var( 'batch_amount' ) );
     54        $interval        = absint( get_post_var( 'batch_interval' ) );
     55        $interval_length = absint( get_post_var( 'batch_interval_length' ) );
    5656
    5757        $batches = floor( $total_contacts / $amount );
  • groundhogg/trunk/admin/funnels/funnels-page.php

    r3394550 r3476690  
    273273            case 'add':
    274274                wp_enqueue_style( 'groundhogg-admin-element' );
     275                wp_enqueue_script( 'groundhogg-admin-data' ); // needed for createState
    275276                break;
    276277            case 'view':
  • groundhogg/trunk/admin/funnels/simulator.php

    r3386122 r3476690  
    1313use function Groundhogg\array_find;
    1414use function Groundhogg\Cli\doing_cli;
     15use function Groundhogg\get_array_var;
    1516use function Groundhogg\get_object_ids;
    1617use function Groundhogg\the_funnel;
     
    2728    protected static $steps = [];
    2829    protected static $is_dry_run = true;
     30
     31    /**
     32     * @var Event
     33     */
     34    protected static $curr_event;
     35
     36    /**
     37     * Allow for the persistence of event arguments during simulation
     38     *
     39     * @var array
     40     */
     41    protected static $event_args = [];
     42
     43    /**
     44     * Allow adding new event args
     45     *
     46     * @param array $args
     47     *
     48     * @return void
     49     */
     50    public static function set_event_args( $args ) {
     51        self::$event_args = array_merge( self::$event_args, $args );
     52    }
     53
     54    /**
     55     * Get an arg from the args array
     56     *
     57     * @param string $arg
     58     * @param        $default
     59     *
     60     * @return bool|mixed
     61     */
     62    public static function get_event_arg( string $arg, $default = false ) {
     63        return get_array_var( self::$event_args, $arg, $default );
     64    }
    2965
    3066    /**
     
    264300                    ] );
    265301
     302                    // add the event args here also
     303                    $event->set_args( self::$event_args );
     304
    266305                    if ( $current->type_is( Send_Email::TYPE ) ) {
    267306                        $event->update( [ 'email_id' => $current->get_meta( 'email_id' ) ] );
     
    271310                    $current->get_step_element()->pre_run( $contact, $event );
    272311                    $result = $current->get_step_element()->run( $contact, $event );
     312
     313                    // update event args if there were any changes
     314                    $event->set_args( self::$event_args );
     315
    273316                    if ( is_wp_error( $result ) ) {
    274317                        self::log( sprintf( "⚠️ %s", $result->get_error_message() ) );
  • groundhogg/trunk/admin/reports/views/contacts.php

    r3343709 r3476690  
    22
    33namespace Groundhogg\Admin\Reports\Views;
     4
     5use function Groundhogg\html;
    46
    57if ( ! defined( 'ABSPATH' ) ) {
     
    5456            <h2 class="title">
    5557                <?php esc_html_e( 'Unsubscribe Reasons', 'groundhogg' ); ?>
    56                 <span class="gh-has-tooltip dashicons dashicons-info">
    57                     <span class="gh-tooltip top">
    58                         <?php esc_html_e( 'This chart shows individual unsubscribe events with their reasons, which may be larger that the total number of unsubscribed contacts.', 'groundhogg' ) ?>
    59                     </span>
    60                 </span>
     58                <?php html()->info_tooltip( esc_html( __( 'This chart shows individual unsubscribe events with their reasons, which may be larger that the total number of unsubscribed contacts.', 'groundhogg' ) ), 'top', true ); ?>
    6159            </h2>
    6260        </div>
  • groundhogg/trunk/assets/js/admin/components/properties.js

    r3352650 r3476690  
    7878    number: {
    7979      name: __('Number', 'groundhogg'),
    80       view: ({ label, ...props }) => {
     80      view: ({ label, decimals = 0, ...props }) => {
     81
     82        let step = (1 / Math.pow(10, decimals)).toString()
    8183        //language=HTML
    8284        return `<label class="property-label" for="${props.id}">${label}</label>${input({
    83             className: 'full-width', ...props, type: 'number',
     85            className: 'full-width', ...props, type: 'number', step
    8486        })}`
     87      },
     88      edit: (field) => {
     89
     90        const { decimals = 0 } = field
     91
     92        //language=HTML
     93        return `
     94            <div class="gh-rows-and-columns">
     95                <div class="gh-row">
     96                    <div class="gh-col">
     97                        <label>${__('Decimals', 'groundhogg')}</label>
     98                        ${input({
     99                type: 'number',
     100                name: 'property_number_decimals',
     101                id: 'property-number-decimals',
     102                value: decimals,
     103                min: 0,
     104                max: 100,
     105            })}
     106                    </div>
     107                </div>
     108            </div>
     109        `
     110      },
     111      onEditMount: (field, updateField) => {
     112        $('#property-number-decimals').on( 'change', (e) => {
     113          updateField({
     114            decimals: e.target.value,
     115          })
     116        })
    85117      },
    86118    },
  • groundhogg/trunk/assets/js/admin/components/properties.min.js

    r3352650 r3476690  
    1 ($=>{const{uuid,specialChars,icons,modal,copyObject,input,textarea,select,tinymceElement,moreMenu,tooltip,inputRepeaterWidget,confirmationModal,dangerConfirmationModal,toggle}=Groundhogg.element;const{sprintf,__,_x,_n}=wp.i18n;const optionsRepeater=({selector,options,onChange})=>{inputRepeaterWidget({selector:selector,rows:options.map(o=>[o]),cellCallbacks:[input],cellProps:[{placeholder:__("Option")}],sortable:true,onChange:r=>{onChange(r.map(r=>r[0]))}}).mount()};const standardField={onMount:({id,name},onChange)=>{$(`#${id}`).on("change",e=>{onChange({[name]:e.target.value})})},edit:()=>{return""},onEditMount:()=>{}};const getFieldType=type=>{return{...standardField,...fieldTypes[type]}};const fieldTypes={text:{name:__("Text","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"text"})}`}},textarea:{name:__("Textarea","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${textarea({className:"full-width",...props})}`}},number:{name:__("Number","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"number"})}`}},url:{name:__("URL","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>
     1($=>{const{uuid,specialChars,icons,modal,copyObject,input,textarea,select,tinymceElement,moreMenu,tooltip,inputRepeaterWidget,confirmationModal,dangerConfirmationModal,toggle}=Groundhogg.element;const{sprintf,__,_x,_n}=wp.i18n;const optionsRepeater=({selector,options,onChange})=>{inputRepeaterWidget({selector:selector,rows:options.map(o=>[o]),cellCallbacks:[input],cellProps:[{placeholder:__("Option")}],sortable:true,onChange:r=>{onChange(r.map(r=>r[0]))}}).mount()};const standardField={onMount:({id,name},onChange)=>{$(`#${id}`).on("change",e=>{onChange({[name]:e.target.value})})},edit:()=>{return""},onEditMount:()=>{}};const getFieldType=type=>{return{...standardField,...fieldTypes[type]}};const fieldTypes={text:{name:__("Text","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"text"})}`}},textarea:{name:__("Textarea","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>${textarea({className:"full-width",...props})}`}},number:{name:__("Number","groundhogg"),view:({label,decimals=0,...props})=>{let step=(1/Math.pow(10,decimals)).toString();return`<label class="property-label" for="${props.id}">${label}</label>${input({className:"full-width",...props,type:"number",step:step})}`},edit:field=>{const{decimals=0}=field;return`
     2            <div class="gh-rows-and-columns">
     3                <div class="gh-row">
     4                    <div class="gh-col">
     5                        <label>${__("Decimals","groundhogg")}</label>
     6                        ${input({type:"number",name:"property_number_decimals",id:"property-number-decimals",value:decimals,min:0,max:100})}
     7                    </div>
     8                </div>
     9            </div>
     10        `},onEditMount:(field,updateField)=>{$("#property-number-decimals").on("change",e=>{updateField({decimals:e.target.value})})}},url:{name:__("URL","groundhogg"),view:({label,...props})=>{return`<label class="property-label" for="${props.id}">${label}</label>
    211        <div class="gh-input-group">${input({className:"full-width",...props,type:"url"})}
    312            <button id="link-${props.id}" class="gh-button secondary small icon">
     
    2433                </div>
    2534            </div>
    26         `},onEditMount:(field,updateField)=>{optionsRepeater({selector:"#property-dropdown-options",options:field.options||[""],onChange:options=>updateField({options:options})})}},dropdown:{name:__("Dropdown","groundhogg"),view:({label,value,options:options=[],...props})=>{options=options.map(o=>({text:o,value:o}));options.unshift({text:__("Select..."),value:""});if(props.multiple&&!Array.isArray(value)){value=value.split(",").map(v=>v.trim())}return`<label class="property-label" for="${props.id}">${label}</label>${select({className:"full-width",options:options,selected:value,...props})}`},onMount:({id,multiple,name,...props},onChange)=>{$(`#${id}`).on("change",e=>{if(multiple){onChange({[name]:[...e.target.selectedOptions].map(o=>o.value)})}else{onChange({[name]:e.target.value})}})},edit:field=>{const{multiple}=field;return`
     35        `},onEditMount:(field,updateField)=>{optionsRepeater({selector:"#property-dropdown-options",options:field.options||[""],onChange:options=>updateField({options:options})})}},dropdown:{name:__("Dropdown","groundhogg"),view:({label,value,options=[],...props})=>{options=options.map(o=>({text:o,value:o}));options.unshift({text:__("Select..."),value:""});if(props.multiple&&!Array.isArray(value)){value=value.split(",").map(v=>v.trim())}return`<label class="property-label" for="${props.id}">${label}</label>${select({className:"full-width",options:options,selected:value,...props})}`},onMount:({id,multiple,name,...props},onChange)=>{$(`#${id}`).on("change",e=>{if(multiple){onChange({[name]:[...e.target.selectedOptions].map(o=>o.value)})}else{onChange({[name]:e.target.value})}})},edit:field=>{const{multiple}=field;return`
    2736            <div class="gh-rows-and-columns">
    2837                <div class="gh-row">
     
    4150          <button id="add-custom-property" class="gh-button secondary">
    4251              ${__("Add custom properties","groundhogg")}
    43           </button>`},groups:({groups,fields:fields=[]},editable)=>{return`
     52          </button>`},groups:({groups,fields=[]},editable)=>{return`
    4453          <div class="property-groups">
    4554              ${groups.map(g=>Templates.group(g,fields.filter(f=>f.group==g.id),editable)).join("")}
     
    7281                  </button>
    7382              </div>
    74           </div>`},addField:field=>{const{type,label,name,id,order:order=10,width:width=2}=field;let editUI="";try{editUI=getFieldType(type).edit(field)}catch(e){console.log(e)}return`
     83          </div>`},addField:field=>{const{type,label,name,id,order=10,width=2}=field;let editUI="";try{editUI=getFieldType(type).edit(field)}catch(e){console.log(e)}return`
    7584          <div class="property-field">
    7685              <h3 class="no-margin-top">${id?__("Edit field","groundhogg"):__("Add field","groundhogg")}</h3>
     
    123132                  </div>
    124133              </div>
    125           </div>`},field:({group,...field})=>{let fieldUI;try{fieldUI=getFieldType(field.type).view(field)}catch(e){console.log(e);fieldUI=`<span class="gh-text danger">${__("This field is corrupted","groundhogg")}</span>`}let{width:width=2}=field;return`
     134          </div>`},field:({group,...field})=>{let fieldUI;try{fieldUI=getFieldType(field.type).view(field)}catch(e){console.log(e);fieldUI=`<span class="gh-text danger">${__("This field is corrupted","groundhogg")}</span>`}let{width=2}=field;return`
    126135          <div class="property-field col-width-${width}" data-group="${group}" data-id="${field.id}">
    127136              ${fieldUI}
    128           </div>`}};const Properties=(selector,{properties:properties={groups:[],fields:[]},values:values={},onPropertiesUpdated:onPropertiesUpdated=properties=>{},onChange:onChange=properties=>{},canEdit:canEdit=()=>true})=>{properties=copyObject(properties);values=copyObject(values);function isInternalNameInUse(name,fieldId){if(!properties||typeof properties.fields==="undefined"){return false}return properties.fields.some(field=>field.name===name&&field.id!==fieldId)}const removeGroup=id=>{const{fields:fields=[],groups:groups=[]}=properties;properties={...properties,groups:[...groups.filter(g=>g.id!=id)],fields:[...fields.filter(f=>f.group!=id)]};onPropertiesUpdated(properties);mount()};const removeField=id=>{properties={...properties,fields:[...properties.fields.filter(f=>f.id!=id)]};onPropertiesUpdated(properties);mount()};const addField=field=>{if(!properties.fields){properties.fields=[]}properties.fields.push({...field,id:uuid()});onPropertiesUpdated(properties);mount()};const editField=(fieldId,field)=>{properties.fields=[...properties.fields.map(f=>f.id===fieldId?field:f)];onPropertiesUpdated(properties);mount()};const editGroup=(groupId,group)=>{properties.groups=[...properties.groups.map(g=>g.id===groupId?{...g,...group}:g)];onPropertiesUpdated(properties);mount()};const moveGroup=(groupId,direction="down")=>{let group=properties.groups.find(g=>g.id==groupId);let index=properties.groups.findIndex(g=>g.id==groupId);properties.groups.splice(index,1);properties.groups.splice(direction==="down"?index+1:index-1,0,group);onPropertiesUpdated(properties);mount()};const addGroup=name=>{if(!properties.groups){properties.groups=[]}let groupId=uuid();properties.groups.push({id:groupId,name:name});onPropertiesUpdated(properties);mount();addOrEditField({type:"text",name:"",label:"",group:groupId},addField)};const addPropertyGroupModal=()=>{const{close}=modal({content:Templates.addPropertyGroup()});let groupName;$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();addGroup(groupName)}})};const renamePropertyGroupModal=groupId=>{let groupName=properties.groups.find(g=>g.id==groupId).name;const{close}=modal({content:Templates.renamePropertyGroup(groupName)});$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();editGroup(groupId,{name:groupName})}})};const addOrEditField=(newField,onDone)=>{let origField=Groundhogg.functions.jsonCopy(newField);const onAddFieldMount=()=>{const sanitizeKey=label=>{return label.toLowerCase().replace(/[^a-z0-9]/g,"_")};const updateField=(props,r=false)=>{newField={...newField,...props};if(r){setContent(Templates.addField(newField));onAddFieldMount()}};$("#property-field-label").on("input change",e=>{let label=e.target.value;if(onDone===addField){let name=sanitizeKey(label);updateField({label:label,name:name});$("#property-field-name").val(name)}else{updateField({label:label})}});$("#property-field-name").on("input change",e=>{updateField({name:e.target.value})});$("#property-field-order").on("input change",e=>{updateField({order:parseInt(e.target.value)})});$("#property-field-width").on("change",e=>{updateField({width:parseInt(e.target.value)})});$("#property-field-group").select2({data:properties.groups.map(({id,name})=>({id:id,text:name,selected:newField.group===id})),multiple:false}).on("change",e=>newField.group=e.target.value);$("#property-field-type").on("change",e=>{newField.type=e.target.value;setContent(Templates.addField(newField));onAddFieldMount();$("#property-field-type").focus()});$("#create-property-field").on("click",e=>{if(isInternalNameInUse(newField.name,newField.id)){Groundhogg.element.errorDialog({message:`The internal name <code>${newField.name}</code> is already in use.`});return}if(onDone!==addField&&newField.name!==origField.name){dangerConfirmationModal({alert:`<p>Changing the internal name of a custom field might break replacement code references and other uses.</p>
    129               <p>Are you sure you want to continue?</p>`,confirmText:"Continue",onConfirm:()=>{onDone(newField);close()},onCancel:()=>{updateField({name:origField.name});$("#property-field-name").val(origField.name)}});return}onDone(newField);close()});try{getFieldType(newField.type).onEditMount(newField,updateField)}catch(e){console.log(e)}};const{close,setContent}=modal({content:Templates.addField(newField)});onAddFieldMount()};const mount=()=>{if(!properties||!properties.groups||!properties.groups.length){if(!canEdit()){return}$(selector).html(Templates.noProperties());$("#add-custom-property").on("click",e=>{e.preventDefault();addPropertyGroupModal()});return}const{fields:fields=[]}=properties;$(selector).html(Templates.groups({...properties,fields:fields.map(f=>({...f,value:values[f.name]||""}))},canEdit()));onMount()};const onMount=()=>{const{fields:fields=[]}=properties;fields.forEach(f=>{try{getFieldType(f.type).onMount(f,props=>{values={...values,...props};onChange(props)})}catch(e){console.log(e)}});$(".property-field").on("dblclick",e=>{if(!canEdit()){return}moreMenu(e.currentTarget,{items:[{key:"edit",text:__("Edit field","groundhogg")},{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{const fieldId=e.currentTarget.dataset.id;let field=properties.fields.find(f=>f.id==fieldId);switch(k){case"edit":addOrEditField({...field},field=>{editField(fieldId,field)});break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property?","groundhogg")}</p>`,onConfirm:()=>{removeField(fieldId)}});break}}})});$(".property-group-add-field").on("click",e=>{if(!canEdit()){return}const groupId=e.currentTarget.dataset.id;let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField)});$(".property-group-more").on("click",e=>{const groupId=e.currentTarget.dataset.id;let index=properties.groups.findIndex(g=>g.id==groupId);moreMenu(e.currentTarget,{items:[{key:"add-field",text:__("Add Field","groundhogg")},{key:"add-group",text:__("Add Group","groundhogg")},{key:"edit-fields",text:__("Edit Fields","groundhogg")},{key:"rename",text:__("Rename")},index!==0?{key:"move_up",text:__("Move up","groundhogg")}:null,index<properties.groups.length-1?{key:"move_down",text:__("Move down","groundhogg")}:null,{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{switch(k){case"edit-fields":confirmationModal({alert:`<p>${__("Double click a field to edit it!","groundhogg")}</p>`,confirmText:__("Got it!","groundhogg"),closeText:""});break;case"move_up":moveGroup(groupId,"up");break;case"move_down":moveGroup(groupId,"down");break;case"add-group":addPropertyGroupModal();break;case"add-field":let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField);break;case"rename":renamePropertyGroupModal(groupId);break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property group?","groundhogg")}</p>`,onConfirm:()=>{removeGroup(groupId)}});break}}})})};mount()};Groundhogg.propertiesEditor=Properties})(jQuery);
     137          </div>`}};const Properties=(selector,{properties={groups:[],fields:[]},values={},onPropertiesUpdated=properties=>{},onChange=properties=>{},canEdit=()=>true})=>{properties=copyObject(properties);values=copyObject(values);function isInternalNameInUse(name,fieldId){if(!properties||typeof properties.fields==="undefined"){return false}return properties.fields.some(field=>field.name===name&&field.id!==fieldId)}const removeGroup=id=>{const{fields=[],groups=[]}=properties;properties={...properties,groups:[...groups.filter(g=>g.id!=id)],fields:[...fields.filter(f=>f.group!=id)]};onPropertiesUpdated(properties);mount()};const removeField=id=>{properties={...properties,fields:[...properties.fields.filter(f=>f.id!=id)]};onPropertiesUpdated(properties);mount()};const addField=field=>{if(!properties.fields){properties.fields=[]}properties.fields.push({...field,id:uuid()});onPropertiesUpdated(properties);mount()};const editField=(fieldId,field)=>{properties.fields=[...properties.fields.map(f=>f.id===fieldId?field:f)];onPropertiesUpdated(properties);mount()};const editGroup=(groupId,group)=>{properties.groups=[...properties.groups.map(g=>g.id===groupId?{...g,...group}:g)];onPropertiesUpdated(properties);mount()};const moveGroup=(groupId,direction="down")=>{let group=properties.groups.find(g=>g.id==groupId);let index=properties.groups.findIndex(g=>g.id==groupId);properties.groups.splice(index,1);properties.groups.splice(direction==="down"?index+1:index-1,0,group);onPropertiesUpdated(properties);mount()};const addGroup=name=>{if(!properties.groups){properties.groups=[]}let groupId=uuid();properties.groups.push({id:groupId,name:name});onPropertiesUpdated(properties);mount();addOrEditField({type:"text",name:"",label:"",group:groupId},addField)};const addPropertyGroupModal=()=>{const{close}=modal({content:Templates.addPropertyGroup()});let groupName;$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();addGroup(groupName)}})};const renamePropertyGroupModal=groupId=>{let groupName=properties.groups.find(g=>g.id==groupId).name;const{close}=modal({content:Templates.renamePropertyGroup(groupName)});$("#property-group-name").on("change input",e=>{groupName=e.target.value});$("#create-property-group").on("click",e=>{if(groupName.length){close();editGroup(groupId,{name:groupName})}})};const addOrEditField=(newField,onDone)=>{let origField=Groundhogg.functions.jsonCopy(newField);const onAddFieldMount=()=>{const sanitizeKey=label=>{return label.toLowerCase().replace(/[^a-z0-9]/g,"_")};const updateField=(props,r=false)=>{newField={...newField,...props};if(r){setContent(Templates.addField(newField));onAddFieldMount()}};$("#property-field-label").on("input change",e=>{let label=e.target.value;if(onDone===addField){let name=sanitizeKey(label);updateField({label:label,name:name});$("#property-field-name").val(name)}else{updateField({label:label})}});$("#property-field-name").on("input change",e=>{updateField({name:e.target.value})});$("#property-field-order").on("input change",e=>{updateField({order:parseInt(e.target.value)})});$("#property-field-width").on("change",e=>{updateField({width:parseInt(e.target.value)})});$("#property-field-group").select2({data:properties.groups.map(({id,name})=>({id:id,text:name,selected:newField.group===id})),multiple:false}).on("change",e=>newField.group=e.target.value);$("#property-field-type").on("change",e=>{newField.type=e.target.value;setContent(Templates.addField(newField));onAddFieldMount();$("#property-field-type").focus()});$("#create-property-field").on("click",e=>{if(isInternalNameInUse(newField.name,newField.id)){Groundhogg.element.errorDialog({message:`The internal name <code>${newField.name}</code> is already in use.`});return}if(onDone!==addField&&newField.name!==origField.name){dangerConfirmationModal({alert:`<p>Changing the internal name of a custom field might break replacement code references and other uses.</p>
     138              <p>Are you sure you want to continue?</p>`,confirmText:"Continue",onConfirm:()=>{onDone(newField);close()},onCancel:()=>{updateField({name:origField.name});$("#property-field-name").val(origField.name)}});return}onDone(newField);close()});try{getFieldType(newField.type).onEditMount(newField,updateField)}catch(e){console.log(e)}};const{close,setContent}=modal({content:Templates.addField(newField)});onAddFieldMount()};const mount=()=>{if(!properties||!properties.groups||!properties.groups.length){if(!canEdit()){return}$(selector).html(Templates.noProperties());$("#add-custom-property").on("click",e=>{e.preventDefault();addPropertyGroupModal()});return}const{fields=[]}=properties;$(selector).html(Templates.groups({...properties,fields:fields.map(f=>({...f,value:values[f.name]||""}))},canEdit()));onMount()};const onMount=()=>{const{fields=[]}=properties;fields.forEach(f=>{try{getFieldType(f.type).onMount(f,props=>{values={...values,...props};onChange(props)})}catch(e){console.log(e)}});$(".property-field").on("dblclick",e=>{if(!canEdit()){return}moreMenu(e.currentTarget,{items:[{key:"edit",text:__("Edit field","groundhogg")},{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{const fieldId=e.currentTarget.dataset.id;let field=properties.fields.find(f=>f.id==fieldId);switch(k){case"edit":addOrEditField({...field},field=>{editField(fieldId,field)});break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property?","groundhogg")}</p>`,onConfirm:()=>{removeField(fieldId)}});break}}})});$(".property-group-add-field").on("click",e=>{if(!canEdit()){return}const groupId=e.currentTarget.dataset.id;let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField)});$(".property-group-more").on("click",e=>{const groupId=e.currentTarget.dataset.id;let index=properties.groups.findIndex(g=>g.id==groupId);moreMenu(e.currentTarget,{items:[{key:"add-field",text:__("Add Field","groundhogg")},{key:"add-group",text:__("Add Group","groundhogg")},{key:"edit-fields",text:__("Edit Fields","groundhogg")},{key:"rename",text:__("Rename")},index!==0?{key:"move_up",text:__("Move up","groundhogg")}:null,index<properties.groups.length-1?{key:"move_down",text:__("Move down","groundhogg")}:null,{key:"delete",text:`<span class="gh-text danger">${__("Delete")}</span>`}],onSelect:k=>{switch(k){case"edit-fields":confirmationModal({alert:`<p>${__("Double click a field to edit it!","groundhogg")}</p>`,confirmText:__("Got it!","groundhogg"),closeText:""});break;case"move_up":moveGroup(groupId,"up");break;case"move_down":moveGroup(groupId,"down");break;case"add-group":addPropertyGroupModal();break;case"add-field":let newField={type:"text",name:"",label:"",group:groupId};addOrEditField(newField,addField);break;case"rename":renamePropertyGroupModal(groupId);break;case"delete":dangerConfirmationModal({alert:`<p>${__("Are you sure you want to delete this property group?","groundhogg")}</p>`,onConfirm:()=>{removeGroup(groupId)}});break}}})})};mount()};Groundhogg.propertiesEditor=Properties})(jQuery);
  • groundhogg/trunk/assets/js/admin/filters/filters.js

    r3269144 r3476690  
    153153
    154154  const pastDateRanges = {
    155     'any'       : __('At any time', 'groundhogg'),
    156     'today'     : __('Today', 'groundhogg'),
    157     'yesterday' : __('Yesterday', 'groundhogg'),
    158     'this_week' : __('This week', 'groundhogg'),
    159     'last_week' : __('Last week', 'groundhogg'),
    160     'this_month': __('This month', 'groundhogg'),
    161     'last_month': __('Last month', 'groundhogg'),
    162     'this_year' : __('This year', 'groundhogg'),
    163     '24_hours'  : __('In the last 24 hours', 'groundhogg'),
    164     '7_days'    : __('In the last 7 days', 'groundhogg'),
    165     '14_days'   : __('In the last 14 days', 'groundhogg'),
    166     '30_days'   : __('In the last 30 days', 'groundhogg'),
    167     '60_days'   : __('In the last 60 days', 'groundhogg'),
    168     '90_days'   : __('In the last 90 days', 'groundhogg'),
    169     '365_days'  : __('In the last 365 days', 'groundhogg'),
    170     'x_days'    : __('In the last X days', 'groundhogg'),
    171     'before'    : __('Before', 'groundhogg'),
    172     'after'     : __('After', 'groundhogg'),
    173     'between'   : __('Between', 'groundhogg'),
    174     'day_of'    : __('Day of', 'groundhogg'),
     155    'any'         : __('At any time', 'groundhogg'),
     156    'today'       : __('Today', 'groundhogg'),
     157    'yesterday'   : __('Yesterday', 'groundhogg'),
     158    'this_week'   : __('This week', 'groundhogg'),
     159    'last_week'   : __('Last week', 'groundhogg'),
     160    'this_month'  : __('This month', 'groundhogg'),
     161    'last_month'  : __('Last month', 'groundhogg'),
     162    'this_quarter': __('This quarter', 'groundhogg'),
     163    'last_quarter': __('Last quarter', 'groundhogg'),
     164    'this_year'   : __('This year', 'groundhogg'),
     165    'last_year'   : __('Last year', 'groundhogg'),
     166    '24_hours'    : __('In the last 24 hours', 'groundhogg'),
     167    '7_days'      : __('In the last 7 days', 'groundhogg'),
     168    '14_days'     : __('In the last 14 days', 'groundhogg'),
     169    '30_days'     : __('In the last 30 days', 'groundhogg'),
     170    '60_days'     : __('In the last 60 days', 'groundhogg'),
     171    '90_days'     : __('In the last 90 days', 'groundhogg'),
     172    '365_days'    : __('In the last 365 days', 'groundhogg'),
     173    'x_days'      : __('In the last X days', 'groundhogg'),
     174    'before'      : __('Before', 'groundhogg'),
     175    'after'       : __('After', 'groundhogg'),
     176    'between'     : __('Between', 'groundhogg'),
     177    'day_of'      : __('Day of', 'groundhogg'),
    175178  }
    176179
     
    180183    'tomorrow'     : __('Tomorrow', 'groundhogg'),
    181184    'this_week'    : __('This week', 'groundhogg'),
     185    'next_week'    : __('Next week', 'groundhogg'),
    182186    'this_month'   : __('This month', 'groundhogg'),
     187    'next_month'   : __('Next month', 'groundhogg'),
     188    'this_quarter' : __('This quarter', 'groundhogg'),
     189    'next_quarter' : __('Next quarter', 'groundhogg'),
    183190    'this_year'    : __('This year', 'groundhogg'),
     191    'next_year'    : __('Next year', 'groundhogg'),
    184192    'next_24_hours': __('In the next 24 hours', 'groundhogg'),
    185193    'next_7_days'  : __('In the next 7 days', 'groundhogg'),
  • groundhogg/trunk/assets/js/admin/filters/filters.min.js

    r3269144 r3476690  
    1 ($=>{const{searchOptionsWidget,bold,uuid,regexp,andList,clickedIn,orList}=Groundhogg.element;const{Div,Button,ItemPicker,Fragment,Input,Select,Span,Ellipses,Dashicon,InputGroup,ToolTip}=MakeEl;const{formatNumber,formatTime,formatDate,formatDateTime}=Groundhogg.formatting;const{sprintf,__,_x,_n}=wp.i18n;const{base64_json_encode}=Groundhogg.functions;const AllComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const StringComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const NumericComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg")};const ComparisonsTitleGenerators={equals:(k,v)=>sprintf(_x("%1$s equals %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_equals:(k,v)=>sprintf(_x("%1$s does not equal %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),contains:(k,v)=>sprintf(_x("%1$s contains %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_contains:(k,v)=>sprintf(_x("%1$s does not contain %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),starts_with:(k,v)=>sprintf(_x("%1$s starts with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),ends_with:(k,v)=>sprintf(_x("%1$s ends with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_start_with:(k,v)=>sprintf(_x("%1$s does not start with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_end_with:(k,v)=>sprintf(_x("%1$s does not end with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than:(k,v)=>sprintf(_x("%1$s is less than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is less than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than:(k,v)=>sprintf(_x("%1$s is greater than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is greater than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),in:(k,v)=>sprintf(_x("%1$s is %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_in:(k,v)=>sprintf(_x("%1$s is not %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),empty:(k,v)=>sprintf(_x("%1$s is empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_empty:(k,v)=>sprintf(_x("%1$s is not empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),includes:(k,v)=>sprintf(_x("%1$s includes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),excludes:(k,v)=>sprintf(_x("%1$s excludes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),before:(k,v)=>sprintf(_x("%1$s is before %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),day_of:(k,v)=>sprintf(_x("%1$s on %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),after:(k,v)=>sprintf(_x("%1$s is after %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),between:(k,v,v2)=>sprintf(_x("%1$s is between %2$s and %3$s","%1 is a key and %2 and %3 are user defined values","groundhogg"),k,v,v2)};const pastDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),yesterday:__("Yesterday","groundhogg"),this_week:__("This week","groundhogg"),last_week:__("Last week","groundhogg"),this_month:__("This month","groundhogg"),last_month:__("Last month","groundhogg"),this_year:__("This year","groundhogg"),"24_hours":__("In the last 24 hours","groundhogg"),"7_days":__("In the last 7 days","groundhogg"),"14_days":__("In the last 14 days","groundhogg"),"30_days":__("In the last 30 days","groundhogg"),"60_days":__("In the last 60 days","groundhogg"),"90_days":__("In the last 90 days","groundhogg"),"365_days":__("In the last 365 days","groundhogg"),x_days:__("In the last X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const futureDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),tomorrow:__("Tomorrow","groundhogg"),this_week:__("This week","groundhogg"),this_month:__("This month","groundhogg"),this_year:__("This year","groundhogg"),next_24_hours:__("In the next 24 hours","groundhogg"),next_7_days:__("In the next 7 days","groundhogg"),next_14_days:__("In the next 14 days","groundhogg"),next_30_days:__("In the next 30 days","groundhogg"),next_60_days:__("In the next 60 days","groundhogg"),next_90_days:__("In the next 90 days","groundhogg"),next_365_days:__("In the next 365 days","groundhogg"),next_x_days:__("In the next X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const allDateRanges={...pastDateRanges,...futureDateRanges};const activityFilterComparisons={equals:_x("Exactly","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),greater_than:_x("More than","comparison","groundhogg"),less_than_or_equal_to:_x("At most","comparison","groundhogg"),greater_than_or_equal_to:_x("At least","comparison","groundhogg")};const filterCountComparisons={equals:v=>sprintf(_n("%s time","%s times",parseInt(v),"groundhogg"),v),less_than:v=>sprintf(_n("less than %s time","less than %s times",parseInt(v),"groundhogg"),v),less_than_or_equal_to:v=>sprintf(_n("at most %s time","at most %s times",parseInt(v),"groundhogg"),v),greater_than:v=>sprintf(_n("more than %s time","more than %s times",parseInt(v),"groundhogg"),v),greater_than_or_equal_to:v=>sprintf(_n("at least %s time","at least %s times",parseInt(v),"groundhogg"),v)};const moreComparisonTitleGenerators={all_checked:(prefix,options)=>sprintf(__("%2$s is checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),not_checked:(prefix,options)=>sprintf(__("%2$s is not checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_in:(prefix,options)=>sprintf(__("%2$s is selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_not_in:(prefix,options)=>sprintf(__("%2$s is not selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b))))};const createGroup=(id,name)=>({id:id,name:name});const createFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}},defaults={})=>({type:type,name:name,group:group,edit:edit,display:display,preload:preload,defaults:defaults});const createStringFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:StringComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value},true)}),["empty","not_empty"].includes(compare)?null:Input({id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(value)))},preload:preload},{value:"",compare:"equals",...defaults});const createNumberFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"number",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatNumber(value))))},preload:preload},{value:"",compare:"equals",...defaults});const createTimeFilter=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"time",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatTime(value))))},preload:preload},{value:"",compare:"equals",...defaults});const dateFilterFactory=(type,name,group,{edit:edit=()=>null,display:display=()=>null,preload:preload=()=>{}}={},defaults={},dateRanges={})=>createFilter(type,name,group,{edit:({date_range,compare:compare="is",before,after,updateFilter,days:days=0,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),InputGroup([Select({id:"filter-compare",name:"compare",options:{is:"Is",is_not:"Is not"},selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Select({id:"filter-date-range",name:"date_range",options:dateRanges,selected:date_range,onChange:e=>updateFilter({date_range:e.target.value},true)})]),["after","between","day_of"].includes(date_range)?Input({type:"date",value:after.split(" ")[0],id:"filter-after",onChange:e=>updateFilter({after:e.target.value})}):null,date_range==="before"||date_range==="between"?Input({type:"date",value:before.split(" ")[0],id:"filter-before",onChange:e=>updateFilter({before:e.target.value})}):null,date_range==="x_days"||date_range==="next_x_days"?Input({type:"number",value:days,name:"days",id:"filter-days",onChange:e=>updateFilter({days:parseInt(e.target.value)})}):null]),display:({compare:compare="is",date_range,after,before,days:days=0,...rest})=>{let prefix=display(rest);if(!prefix||prefix.length===0){prefix=bold(name)}if(compare==="is_not"){prefix+=" is not"}switch(date_range){case"between":return ComparisonsTitleGenerators.between(prefix,formatDate(after),formatDate(before));case"after":case"day_of":return ComparisonsTitleGenerators[date_range](prefix,formatDate(after));case"before":return ComparisonsTitleGenerators.before(prefix,formatDate(before));default:return sprintf("%s %s",prefix,dateRanges[date_range??"any"]?.replace("X",days).toLowerCase())}},preload:preload},{...defaults,before:"",after:"",days:0,compare:"is"});const createDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},allDateRanges);const createPastDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},pastDateRanges);const createFutureDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"next_24_hours",...defaults},futureDateRanges);const createSelectFilter=(type,name,group,options)=>createFilter(type,name,group,{display:({value})=>sprintf("%s is %s",bold(name),bold(options[value])),edit:({value,updateFilter})=>Select({id:"filter-select",options:options,selected:value,onChange:e=>updateFilter({value:e.target.value})})},{value:Object.keys(options)[0]});const OptionsPicker=({field,options,updateFilter})=>ItemPicker({id:"filter-options",noneSelected:"Type to search...",selected:options.map(opt=>({id:opt,text:opt})),fetchOptions:async search=>field.options.filter(opt=>opt.match(new RegExp(search,"i"))).map(opt=>({id:opt,text:opt})),onChange:items=>updateFilter({options:items.map(item=>item.id)})});const filterFactory={text:({id,label,group})=>createStringFilter(id,label,group),url:({id,label,group})=>createStringFilter(id,label,group),custom_email:({id,label,group})=>createStringFilter(id,label,group),tel:({id,label,group})=>createStringFilter(id,label,group),textarea:({id,label,group})=>createStringFilter(id,label,group),number:({id,label,group})=>createNumberFilter(id,label,group),date:({id,label,group})=>createDateFilter(id,label,group),datetime:({id,label,group})=>createDateFilter(id,label,group),time:({id,label,group})=>createTimeFilter(id,label,group),radio:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))},{compare:"in",options:[]}),dropdown:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:field.multiple?{all_in:__("Has all selected"),all_not_in:__("Does not have all selected")}:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>{if(ComparisonsTitleGenerators[compare]){return ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))}return moreComparisonTitleGenerators[compare](bold(label),options)}},{compare:field.multiple?"all_in":"in",options:[]}),checkboxes:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{all_checked:__("Is Checked","groundhogg-better-meta"),not_checked:__("Is Not Checked","groundhogg-better-meta")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options:options=[],compare:compare=""})=>moreComparisonTitleGenerators[compare](bold(label),options)},{compare:"all_checked",options:[]})};const FilterRegistry=({groups:groups=[],filters:filters=[]}={})=>({groups:groups.reduce((carr,curr)=>{carr[curr.id]=curr.name;return carr},{}),filters:filters.reduce((filters,filter)=>{filters[filter.type]=filter;return filters},{}),registerGroup(group,name){if(group&&name){this.groups[group]=name}else{this.groups[group.id]=group.name}},registerFilter(filter){this.filters[filter.type]=filter},displayName(filter){let name=this.getFilter(filter).display(filter);if(!name){name=this.getFilter(filter).name}return name},displayFilters(filters){return filters.map(group=>group.map(filter=>{return Div({},this.display(filter)).innerHTML}).join(" and ")).join(" or ")},filterName(filter){return this.getFilter(filter).name},display(filter){return this.getFilter(filter).display(filter)},edit(filter,updateFilter){return this.getFilter(filter).edit({...filter,updateFilter:updateFilter})},getFilter({type}){return this.filters[type]??{}},hasFilter({type}){return type in this.filters},preloadFilter(filter){return this.getFilter(filter).preload(filter)},preloadFilters(filters){const promises=[];filters.forEach(filterGroup=>filterGroup.forEach(filter=>{try{const promise=this.preloadFilter(filter);if(promise){promises.push(promise)}}catch(err){}}));return Promise.all(promises)},registerFromProperties(properties){const{tabs,fields,groups}=properties;Object.values(tabs).forEach(t=>{Object.values(groups).filter(f=>f.tab===t.id).forEach(s=>{let groupId=`${t.id}-${s.id}`;this.registerGroup(groupId,`${t.name}: ${s.name}`);Object.values(fields).filter(f=>f.group===s.id).forEach(f=>{if(f.type in filterFactory){this.registerFilter(filterFactory[f.type]({...f,group:groupId}))}})})})},registerFromConfig({stringColumns:stringColumns={},numberColumns:numberColumns={},dateColumns:dateColumns={},futureDateColumns:futureDateColumns={},pastDateColumns:pastDateColumns={},selectColumns:selectColumns={},name:name="",group:group="table"}){this.registerGroup(group,name);for(let column in stringColumns){this.registerFilter(createStringFilter(column,stringColumns[column],group))}for(let column in numberColumns){this.registerFilter(createNumberFilter(column,numberColumns[column],group))}for(let column in selectColumns){this.registerFilter(createSelectFilter(column,selectColumns[column][0],group,selectColumns[column][1]))}for(let column in dateColumns){this.registerFilter(createDateFilter(column,dateColumns[column],group),{display:()=>bold(name)})}for(let column in futureDateColumns){this.registerFilter(createFutureDateFilter(column,futureDateColumns[column],group),{display:()=>bold(name)})}for(let column in pastDateColumns){this.registerFilter(createPastDateFilter(column,pastDateColumns[column],group),{display:()=>bold(name)})}return this}});const Filters=({id,filterRegistry:filterRegistry=FilterRegistry({}),filters:filters=[],onChange:onChange=filters=>{}})=>{if(!filters){filters=[]}filters.forEach(filterGroup=>filterGroup.forEach(filter=>{if(!filter.id){filter.id=uuid()}}));const morph=()=>{try{morphdom(document.getElementById(id),FiltersEditor())}catch(e){console.log(e)}};const State=Groundhogg.createState({preloaded:false,activeFilter:null});const setState=(newState,doMorph=true)=>{State.set(newState);if(doMorph){morph()}};const FilterBroken=(filter,group,index,err)=>{let message;if(filterRegistry.hasFilter(filter)){message=err instanceof Error?err.message:sprintf(__("This %s filter is corrupted","groundhogg"),bold(filterRegistry.filterName(filter)))}else{message=sprintf(__("This %s filter is not available.","groundhogg"),bold(filter.type))}return Div({id:`filter-${filter.id}`,className:"filter filter-view filter-broken",tabindex:0,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)}},[Span({className:"filter-name text"},message),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>{e.preventDefault();deleteFilter(group,index)}},Dashicon("no-alt"))])};const Filter=(filter,group,index)=>Div({id:`filter-${filter.id}`,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)},className:"filter filter-view",tabindex:0},[Span({className:"filter-name text"},filterRegistry.displayName(filter)),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("no-alt"))]);const EditFilter=(filter,group,index)=>{let tempFilterSettings={...filter};const morphFilter=()=>{try{morphdom(document.getElementById(`filter-${id}-settings`),FilterSettings())}catch(e){}};const updateTempFilterSettings=(newSettings,doMorph=false)=>{tempFilterSettings={...tempFilterSettings,...newSettings};if(doMorph){morphFilter()}};const FilterSettings=()=>Div({id:`filter-${id}-settings`,className:"settings"},filterRegistry.edit(tempFilterSettings,updateTempFilterSettings));return Div({id:`edit-filter-${filter.id}`,className:`filter filter-edit-wrap filter-${filter.type}`,tabindex:0},Div({className:"filter-edit"},[Div({className:"header"},[bold(filterRegistry.filterName(filter)),Button({type:"button",className:"close-edit",onClick:e=>editFilter(null)},Dashicon("no-alt"))]),FilterSettings(),Div({className:"actions"},[Button({type:"button",id:`delete-${group}-${index}`,className:"delete delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("trash")),Button({type:"button",id:`commit-${group}-${index}`,className:"commit commit-filter",onClick:e=>updateFilter(tempFilterSettings,group,index)},Dashicon("yes"))])]))};const FilterGroup=(filters,group)=>Div({id:`group-${id}-${group}`,className:"group"},[...filters.map((filter,index)=>{try{if(State.activeFilter===filter.id){return EditFilter(filter,group,index)}return Filter(filter,group,index)}catch(err){return FilterBroken(filter,group,index,err)}}),Button({type:"button",id:`add-filter-to-${id}-${group}`,className:"add-filter gh-has-tooltip",onClick:e=>{let options=Object.values(filterRegistry.filters);let groups=filterRegistry.groups;searchOptionsWidget({position:"fixed",target:e.currentTarget,options:options,groups:groups,onSelect:option=>{let newFilter={type:option.type,...option.defaults};console.log(newFilter);addFilter(newFilter,group)},filterOption:(option,search)=>{return option.name.match(regexp(search))},renderOption:option=>option.name,noOptions:__("No matching filters...","groundhogg")}).mount()}},[Dashicon(group===0&&!filters.length?"filter":"plus-alt2"),ToolTip(__("Add a filter","groundhogg"),"right")])]);const addFilter=(filter,group)=>{filter={id:uuid(),...filter};if(filters[group]){filters[group].push(filter)}else{filters.push([filter])}editFilter(filter.id)};const deleteFilter=(group,index)=>{filters[group].splice(index,1);if(!filters[group].length){filters.splice(group,1)}editFilter(null);onChange(filters)};const updateFilter=(newFilter,group,index)=>{filters[group][index]={...filters[group][index],...newFilter};editFilter(null);onChange(filters)};const editFilter=id=>{setState({activeFilter:id})};const GroupSeparator=after=>Div({id:`after-${id}-${after}`,className:"or-separator"},Span({className:"or-circle"},_x("Or...","search filters separator","groundhogg")));const FiltersLoading=()=>Div({id:id,className:`search-filters`},Span({id:`${id}-loading`,className:"filters-loading"},Ellipses(__("Loading"))));const FiltersEditor=()=>{if(!State.get("preloaded")){filterRegistry.preloadFilters(filters).finally(()=>setState({preloaded:true}));return FiltersLoading()}const groups=[];filters.forEach((filterGroup,i)=>{groups.push(FilterGroup(filterGroup,i));groups.push(GroupSeparator(i))});return Div({id:id,className:`search-filters-editor`},[...groups,FilterGroup([],filters.length)])};return FiltersEditor()};const FilterDisplay=({filters,filterRegistry})=>{const renderFilters=()=>Span({},filters.map(row=>{return row.map(filter=>{let result=filterRegistry.displayName(filter);return Span({},result).innerHTML}).join(" <i>AND</i> ")}).join(" <br/><i>OR</i> "));try{return renderFilters()}catch(err){}let el=Span({},["Loading..."]);filterRegistry.preloadFilters(filters).finally(r=>{morphdom(el,renderFilters())});return el};Groundhogg.filters.Filters=Filters;Groundhogg.filters.FilterDisplay=FilterDisplay;Groundhogg.filters.FilterRegistry=FilterRegistry;Groundhogg.filters.createFilter=createFilter;Groundhogg.filters.createGroup=createGroup;Groundhogg.filters.createStringFilter=createStringFilter;Groundhogg.filters.createNumberFilter=createNumberFilter;Groundhogg.filters.createTimeFilter=createTimeFilter;Groundhogg.filters.createPastDateFilter=createPastDateFilter;Groundhogg.filters.createFutureDateFilter=createFutureDateFilter;Groundhogg.filters.createDateFilter=createDateFilter;Groundhogg.filters.createSelectFilter=createSelectFilter;Groundhogg.filters.comparisons={ComparisonsTitleGenerators:ComparisonsTitleGenerators,AllComparisons:AllComparisons,StringComparisons:StringComparisons,NumericComparisons:NumericComparisons,pastDateRanges:pastDateRanges,futureDateRanges:futureDateRanges,allDateRanges:allDateRanges,moreComparisonTitleGenerators:moreComparisonTitleGenerators};if(window.GroundhoggTableFilters){const{id:id="",filters:filters=[],...TableFilterConfig}=GroundhoggTableFilters;const TableFilterRegistry=FilterRegistry({});TableFilterRegistry.registerFromConfig(TableFilterConfig);GroundhoggTableFilters.FilterRegistry=TableFilterRegistry;$(()=>{let tableFiltersEl=document.getElementById("table-filters");if(tableFiltersEl){tableFiltersEl.replaceWith(Filters({id:id,filterRegistry:TableFilterRegistry,filters:filters,onChange:filters=>document.querySelector('form.search-form input[name="include_filters"]').value=base64_json_encode(filters)}))}})}})(jQuery);
     1($=>{const{searchOptionsWidget,bold,uuid,regexp,andList,clickedIn,orList}=Groundhogg.element;const{Div,Button,ItemPicker,Fragment,Input,Select,Span,Ellipses,Dashicon,InputGroup,ToolTip}=MakeEl;const{formatNumber,formatTime,formatDate,formatDateTime}=Groundhogg.formatting;const{sprintf,__,_x,_n}=wp.i18n;const{base64_json_encode}=Groundhogg.functions;const AllComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const StringComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),contains:_x("Contains","comparison","groundhogg"),not_contains:_x("Does not contain","comparison","groundhogg"),starts_with:_x("Starts with","comparison","groundhogg"),ends_with:_x("Ends with","comparison","groundhogg"),does_not_start_with:_x("Does not start with","comparison","groundhogg"),does_not_end_with:_x("Does not end with","comparison","groundhogg"),empty:_x("Is empty","comparison","groundhogg"),not_empty:_x("Is not empty","comparison","groundhogg")};const NumericComparisons={equals:_x("Equals","comparison","groundhogg"),not_equals:_x("Not equals","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),less_than_or_equal_to:_x("Less than or equal to","comparison","groundhogg"),greater_than:_x("Greater than","comparison","groundhogg"),greater_than_or_equal_to:_x("Greater than or equal to","comparison","groundhogg")};const ComparisonsTitleGenerators={equals:(k,v)=>sprintf(_x("%1$s equals %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_equals:(k,v)=>sprintf(_x("%1$s does not equal %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),contains:(k,v)=>sprintf(_x("%1$s contains %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_contains:(k,v)=>sprintf(_x("%1$s does not contain %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),starts_with:(k,v)=>sprintf(_x("%1$s starts with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),ends_with:(k,v)=>sprintf(_x("%1$s ends with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_start_with:(k,v)=>sprintf(_x("%1$s does not start with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),does_not_end_with:(k,v)=>sprintf(_x("%1$s does not end with %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than:(k,v)=>sprintf(_x("%1$s is less than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),less_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is less than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than:(k,v)=>sprintf(_x("%1$s is greater than %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),greater_than_or_equal_to:(k,v)=>sprintf(_x("%1$s is greater than or equal to %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),in:(k,v)=>sprintf(_x("%1$s is %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_in:(k,v)=>sprintf(_x("%1$s is not %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),empty:(k,v)=>sprintf(_x("%1$s is empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),not_empty:(k,v)=>sprintf(_x("%1$s is not empty","%1 is a key and %2 is user defined value","groundhogg"),k,v),includes:(k,v)=>sprintf(_x("%1$s includes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),excludes:(k,v)=>sprintf(_x("%1$s excludes %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),before:(k,v)=>sprintf(_x("%1$s is before %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),day_of:(k,v)=>sprintf(_x("%1$s on %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),after:(k,v)=>sprintf(_x("%1$s is after %2$s","%1 is a key and %2 is user defined value","groundhogg"),k,v),between:(k,v,v2)=>sprintf(_x("%1$s is between %2$s and %3$s","%1 is a key and %2 and %3 are user defined values","groundhogg"),k,v,v2)};const pastDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),yesterday:__("Yesterday","groundhogg"),this_week:__("This week","groundhogg"),last_week:__("Last week","groundhogg"),this_month:__("This month","groundhogg"),last_month:__("Last month","groundhogg"),this_quarter:__("This quarter","groundhogg"),last_quarter:__("Last quarter","groundhogg"),this_year:__("This year","groundhogg"),last_year:__("Last year","groundhogg"),"24_hours":__("In the last 24 hours","groundhogg"),"7_days":__("In the last 7 days","groundhogg"),"14_days":__("In the last 14 days","groundhogg"),"30_days":__("In the last 30 days","groundhogg"),"60_days":__("In the last 60 days","groundhogg"),"90_days":__("In the last 90 days","groundhogg"),"365_days":__("In the last 365 days","groundhogg"),x_days:__("In the last X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const futureDateRanges={any:__("At any time","groundhogg"),today:__("Today","groundhogg"),tomorrow:__("Tomorrow","groundhogg"),this_week:__("This week","groundhogg"),next_week:__("Next week","groundhogg"),this_month:__("This month","groundhogg"),next_month:__("Next month","groundhogg"),this_quarter:__("This quarter","groundhogg"),next_quarter:__("Next quarter","groundhogg"),this_year:__("This year","groundhogg"),next_year:__("Next year","groundhogg"),next_24_hours:__("In the next 24 hours","groundhogg"),next_7_days:__("In the next 7 days","groundhogg"),next_14_days:__("In the next 14 days","groundhogg"),next_30_days:__("In the next 30 days","groundhogg"),next_60_days:__("In the next 60 days","groundhogg"),next_90_days:__("In the next 90 days","groundhogg"),next_365_days:__("In the next 365 days","groundhogg"),next_x_days:__("In the next X days","groundhogg"),before:__("Before","groundhogg"),after:__("After","groundhogg"),between:__("Between","groundhogg"),day_of:__("Day of","groundhogg")};const allDateRanges={...pastDateRanges,...futureDateRanges};const activityFilterComparisons={equals:_x("Exactly","comparison","groundhogg"),less_than:_x("Less than","comparison","groundhogg"),greater_than:_x("More than","comparison","groundhogg"),less_than_or_equal_to:_x("At most","comparison","groundhogg"),greater_than_or_equal_to:_x("At least","comparison","groundhogg")};const filterCountComparisons={equals:v=>sprintf(_n("%s time","%s times",parseInt(v),"groundhogg"),v),less_than:v=>sprintf(_n("less than %s time","less than %s times",parseInt(v),"groundhogg"),v),less_than_or_equal_to:v=>sprintf(_n("at most %s time","at most %s times",parseInt(v),"groundhogg"),v),greater_than:v=>sprintf(_n("more than %s time","more than %s times",parseInt(v),"groundhogg"),v),greater_than_or_equal_to:v=>sprintf(_n("at least %s time","at least %s times",parseInt(v),"groundhogg"),v)};const moreComparisonTitleGenerators={all_checked:(prefix,options)=>sprintf(__("%2$s is checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),not_checked:(prefix,options)=>sprintf(__("%2$s is not checked for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_in:(prefix,options)=>sprintf(__("%2$s is selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b)))),all_not_in:(prefix,options)=>sprintf(__("%2$s is not selected for %1$s","groundhogg-better-meta"),prefix,andList(options.map(b=>bold(b))))};const createGroup=(id,name)=>({id:id,name:name});const createFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}},defaults={})=>({type:type,name:name,group:group,edit:edit,display:display,preload:preload,defaults:defaults});const createStringFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:StringComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value},true)}),["empty","not_empty"].includes(compare)?null:Input({id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(value)))},preload:preload},{value:"",compare:"equals",...defaults});const createNumberFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"number",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatNumber(value))))},preload:preload},{value:"",compare:"equals",...defaults});const createTimeFilter=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={})=>createFilter(type,name,group,{edit:({value,compare,updateFilter,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),Select({id:"filter-compare",name:"filter_compare",options:NumericComparisons,selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Input({type:"time",id:"filter-value",value:value,onChange:e=>updateFilter({value:e.target.value})})]),display:({compare,value,...rest})=>{return Fragment(ComparisonsTitleGenerators[compare](bold(name),bold(formatTime(value))))},preload:preload},{value:"",compare:"equals",...defaults});const dateFilterFactory=(type,name,group,{edit=()=>null,display=()=>null,preload=()=>{}}={},defaults={},dateRanges={})=>createFilter(type,name,group,{edit:({date_range,compare="is",before,after,updateFilter,days=0,...rest})=>Fragment([edit({...rest,updateFilter:updateFilter}),InputGroup([Select({id:"filter-compare",name:"compare",options:{is:"Is",is_not:"Is not"},selected:compare,onChange:e=>updateFilter({compare:e.target.value})}),Select({id:"filter-date-range",name:"date_range",options:dateRanges,selected:date_range,onChange:e=>updateFilter({date_range:e.target.value},true)})]),["after","between","day_of"].includes(date_range)?Input({type:"date",value:after.split(" ")[0],id:"filter-after",onChange:e=>updateFilter({after:e.target.value})}):null,date_range==="before"||date_range==="between"?Input({type:"date",value:before.split(" ")[0],id:"filter-before",onChange:e=>updateFilter({before:e.target.value})}):null,date_range==="x_days"||date_range==="next_x_days"?Input({type:"number",value:days,name:"days",id:"filter-days",onChange:e=>updateFilter({days:parseInt(e.target.value)})}):null]),display:({compare="is",date_range,after,before,days=0,...rest})=>{let prefix=display(rest);if(!prefix||prefix.length===0){prefix=bold(name)}if(compare==="is_not"){prefix+=" is not"}switch(date_range){case"between":return ComparisonsTitleGenerators.between(prefix,formatDate(after),formatDate(before));case"after":case"day_of":return ComparisonsTitleGenerators[date_range](prefix,formatDate(after));case"before":return ComparisonsTitleGenerators.before(prefix,formatDate(before));default:return sprintf("%s %s",prefix,dateRanges[date_range??"any"]?.replace("X",days).toLowerCase())}},preload:preload},{...defaults,before:"",after:"",days:0,compare:"is"});const createDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},allDateRanges);const createPastDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"24_hours",...defaults},pastDateRanges);const createFutureDateFilter=(type,name,group,callbacks,defaults={})=>dateFilterFactory(type,name,group,callbacks,{date_range:"next_24_hours",...defaults},futureDateRanges);const createSelectFilter=(type,name,group,options)=>createFilter(type,name,group,{display:({value})=>sprintf("%s is %s",bold(name),bold(options[value])),edit:({value,updateFilter})=>Select({id:"filter-select",options:options,selected:value,onChange:e=>updateFilter({value:e.target.value})})},{value:Object.keys(options)[0]});const OptionsPicker=({field,options,updateFilter})=>ItemPicker({id:"filter-options",noneSelected:"Type to search...",selected:options.map(opt=>({id:opt,text:opt})),fetchOptions:async search=>field.options.filter(opt=>opt.match(new RegExp(search,"i"))).map(opt=>({id:opt,text:opt})),onChange:items=>updateFilter({options:items.map(item=>item.id)})});const filterFactory={text:({id,label,group})=>createStringFilter(id,label,group),url:({id,label,group})=>createStringFilter(id,label,group),custom_email:({id,label,group})=>createStringFilter(id,label,group),tel:({id,label,group})=>createStringFilter(id,label,group),textarea:({id,label,group})=>createStringFilter(id,label,group),number:({id,label,group})=>createNumberFilter(id,label,group),date:({id,label,group})=>createDateFilter(id,label,group),datetime:({id,label,group})=>createDateFilter(id,label,group),time:({id,label,group})=>createTimeFilter(id,label,group),radio:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))},{compare:"in",options:[]}),dropdown:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:field.multiple?{all_in:__("Has all selected"),all_not_in:__("Does not have all selected")}:{in:_x("Is one of","comparison, groundhogg"),not_in:_x("Is not one of","comparison","groundhogg")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options,compare})=>{if(ComparisonsTitleGenerators[compare]){return ComparisonsTitleGenerators[compare](bold(label),orList(options.map(o=>bold(o))))}return moreComparisonTitleGenerators[compare](bold(label),options)}},{compare:field.multiple?"all_in":"in",options:[]}),checkboxes:({id,label,group,...field})=>createFilter(id,label,group,{edit:({options,compare,updateFilter})=>Fragment([Select({id:"filter-compare",selected:compare,options:{all_checked:__("Is Checked","groundhogg-better-meta"),not_checked:__("Is Not Checked","groundhogg-better-meta")},onChange:e=>updateFilter({compare:e.target.value})}),OptionsPicker({field:field,options:options,updateFilter:updateFilter})]),display:({options=[],compare=""})=>moreComparisonTitleGenerators[compare](bold(label),options)},{compare:"all_checked",options:[]})};const FilterRegistry=({groups=[],filters=[]}={})=>({groups:groups.reduce((carr,curr)=>{carr[curr.id]=curr.name;return carr},{}),filters:filters.reduce((filters,filter)=>{filters[filter.type]=filter;return filters},{}),registerGroup(group,name){if(group&&name){this.groups[group]=name}else{this.groups[group.id]=group.name}},registerFilter(filter){this.filters[filter.type]=filter},displayName(filter){let name=this.getFilter(filter).display(filter);if(!name){name=this.getFilter(filter).name}return name},displayFilters(filters){return filters.map(group=>group.map(filter=>{return Div({},this.display(filter)).innerHTML}).join(" and ")).join(" or ")},filterName(filter){return this.getFilter(filter).name},display(filter){return this.getFilter(filter).display(filter)},edit(filter,updateFilter){return this.getFilter(filter).edit({...filter,updateFilter:updateFilter})},getFilter({type}){return this.filters[type]??{}},hasFilter({type}){return type in this.filters},preloadFilter(filter){return this.getFilter(filter).preload(filter)},preloadFilters(filters){const promises=[];filters.forEach(filterGroup=>filterGroup.forEach(filter=>{try{const promise=this.preloadFilter(filter);if(promise){promises.push(promise)}}catch(err){}}));return Promise.all(promises)},registerFromProperties(properties){const{tabs,fields,groups}=properties;Object.values(tabs).forEach(t=>{Object.values(groups).filter(f=>f.tab===t.id).forEach(s=>{let groupId=`${t.id}-${s.id}`;this.registerGroup(groupId,`${t.name}: ${s.name}`);Object.values(fields).filter(f=>f.group===s.id).forEach(f=>{if(f.type in filterFactory){this.registerFilter(filterFactory[f.type]({...f,group:groupId}))}})})})},registerFromConfig({stringColumns={},numberColumns={},dateColumns={},futureDateColumns={},pastDateColumns={},selectColumns={},name="",group="table"}){this.registerGroup(group,name);for(let column in stringColumns){this.registerFilter(createStringFilter(column,stringColumns[column],group))}for(let column in numberColumns){this.registerFilter(createNumberFilter(column,numberColumns[column],group))}for(let column in selectColumns){this.registerFilter(createSelectFilter(column,selectColumns[column][0],group,selectColumns[column][1]))}for(let column in dateColumns){this.registerFilter(createDateFilter(column,dateColumns[column],group),{display:()=>bold(name)})}for(let column in futureDateColumns){this.registerFilter(createFutureDateFilter(column,futureDateColumns[column],group),{display:()=>bold(name)})}for(let column in pastDateColumns){this.registerFilter(createPastDateFilter(column,pastDateColumns[column],group),{display:()=>bold(name)})}return this}});const Filters=({id,filterRegistry=FilterRegistry({}),filters=[],onChange=filters=>{}})=>{if(!filters){filters=[]}filters.forEach(filterGroup=>filterGroup.forEach(filter=>{if(!filter.id){filter.id=uuid()}}));const morph=()=>{try{morphdom(document.getElementById(id),FiltersEditor())}catch(e){console.log(e)}};const State=Groundhogg.createState({preloaded:false,activeFilter:null});const setState=(newState,doMorph=true)=>{State.set(newState);if(doMorph){morph()}};const FilterBroken=(filter,group,index,err)=>{let message;if(filterRegistry.hasFilter(filter)){message=err instanceof Error?err.message:sprintf(__("This %s filter is corrupted","groundhogg"),bold(filterRegistry.filterName(filter)))}else{message=sprintf(__("This %s filter is not available.","groundhogg"),bold(filter.type))}return Div({id:`filter-${filter.id}`,className:"filter filter-view filter-broken",tabindex:0,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)}},[Span({className:"filter-name text"},message),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>{e.preventDefault();deleteFilter(group,index)}},Dashicon("no-alt"))])};const Filter=(filter,group,index)=>Div({id:`filter-${filter.id}`,onClick:e=>{if(clickedIn(e,".delete-filter")){return}editFilter(filter.id)},className:"filter filter-view",tabindex:0},[Span({className:"filter-name text"},filterRegistry.displayName(filter)),Button({type:"button",id:`delete-${group}-${index}`,className:"delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("no-alt"))]);const EditFilter=(filter,group,index)=>{let tempFilterSettings={...filter};const morphFilter=()=>{try{morphdom(document.getElementById(`filter-${id}-settings`),FilterSettings())}catch(e){}};const updateTempFilterSettings=(newSettings,doMorph=false)=>{tempFilterSettings={...tempFilterSettings,...newSettings};if(doMorph){morphFilter()}};const FilterSettings=()=>Div({id:`filter-${id}-settings`,className:"settings"},filterRegistry.edit(tempFilterSettings,updateTempFilterSettings));return Div({id:`edit-filter-${filter.id}`,className:`filter filter-edit-wrap filter-${filter.type}`,tabindex:0},Div({className:"filter-edit"},[Div({className:"header"},[bold(filterRegistry.filterName(filter)),Button({type:"button",className:"close-edit",onClick:e=>editFilter(null)},Dashicon("no-alt"))]),FilterSettings(),Div({className:"actions"},[Button({type:"button",id:`delete-${group}-${index}`,className:"delete delete-filter",onClick:e=>deleteFilter(group,index)},Dashicon("trash")),Button({type:"button",id:`commit-${group}-${index}`,className:"commit commit-filter",onClick:e=>updateFilter(tempFilterSettings,group,index)},Dashicon("yes"))])]))};const FilterGroup=(filters,group)=>Div({id:`group-${id}-${group}`,className:"group"},[...filters.map((filter,index)=>{try{if(State.activeFilter===filter.id){return EditFilter(filter,group,index)}return Filter(filter,group,index)}catch(err){return FilterBroken(filter,group,index,err)}}),Button({type:"button",id:`add-filter-to-${id}-${group}`,className:"add-filter gh-has-tooltip",onClick:e=>{let options=Object.values(filterRegistry.filters);let groups=filterRegistry.groups;searchOptionsWidget({position:"fixed",target:e.currentTarget,options:options,groups:groups,onSelect:option=>{let newFilter={type:option.type,...option.defaults};console.log(newFilter);addFilter(newFilter,group)},filterOption:(option,search)=>{return option.name.match(regexp(search))},renderOption:option=>option.name,noOptions:__("No matching filters...","groundhogg")}).mount()}},[Dashicon(group===0&&!filters.length?"filter":"plus-alt2"),ToolTip(__("Add a filter","groundhogg"),"right")])]);const addFilter=(filter,group)=>{filter={id:uuid(),...filter};if(filters[group]){filters[group].push(filter)}else{filters.push([filter])}editFilter(filter.id)};const deleteFilter=(group,index)=>{filters[group].splice(index,1);if(!filters[group].length){filters.splice(group,1)}editFilter(null);onChange(filters)};const updateFilter=(newFilter,group,index)=>{filters[group][index]={...filters[group][index],...newFilter};editFilter(null);onChange(filters)};const editFilter=id=>{setState({activeFilter:id})};const GroupSeparator=after=>Div({id:`after-${id}-${after}`,className:"or-separator"},Span({className:"or-circle"},_x("Or...","search filters separator","groundhogg")));const FiltersLoading=()=>Div({id:id,className:`search-filters`},Span({id:`${id}-loading`,className:"filters-loading"},Ellipses(__("Loading"))));const FiltersEditor=()=>{if(!State.get("preloaded")){filterRegistry.preloadFilters(filters).finally(()=>setState({preloaded:true}));return FiltersLoading()}const groups=[];filters.forEach((filterGroup,i)=>{groups.push(FilterGroup(filterGroup,i));groups.push(GroupSeparator(i))});return Div({id:id,className:`search-filters-editor`},[...groups,FilterGroup([],filters.length)])};return FiltersEditor()};const FilterDisplay=({filters,filterRegistry})=>{const renderFilters=()=>Span({},filters.map(row=>{return row.map(filter=>{let result=filterRegistry.displayName(filter);return Span({},result).innerHTML}).join(" <i>AND</i> ")}).join(" <br/><i>OR</i> "));try{return renderFilters()}catch(err){}let el=Span({},["Loading..."]);filterRegistry.preloadFilters(filters).finally(r=>{morphdom(el,renderFilters())});return el};Groundhogg.filters.Filters=Filters;Groundhogg.filters.FilterDisplay=FilterDisplay;Groundhogg.filters.FilterRegistry=FilterRegistry;Groundhogg.filters.createFilter=createFilter;Groundhogg.filters.createGroup=createGroup;Groundhogg.filters.createStringFilter=createStringFilter;Groundhogg.filters.createNumberFilter=createNumberFilter;Groundhogg.filters.createTimeFilter=createTimeFilter;Groundhogg.filters.createPastDateFilter=createPastDateFilter;Groundhogg.filters.createFutureDateFilter=createFutureDateFilter;Groundhogg.filters.createDateFilter=createDateFilter;Groundhogg.filters.createSelectFilter=createSelectFilter;Groundhogg.filters.comparisons={ComparisonsTitleGenerators:ComparisonsTitleGenerators,AllComparisons:AllComparisons,StringComparisons:StringComparisons,NumericComparisons:NumericComparisons,pastDateRanges:pastDateRanges,futureDateRanges:futureDateRanges,allDateRanges:allDateRanges,moreComparisonTitleGenerators:moreComparisonTitleGenerators};if(window.GroundhoggTableFilters){const{id="",filters=[],...TableFilterConfig}=GroundhoggTableFilters;const TableFilterRegistry=FilterRegistry({});TableFilterRegistry.registerFromConfig(TableFilterConfig);GroundhoggTableFilters.FilterRegistry=TableFilterRegistry;$(()=>{let tableFiltersEl=document.getElementById("table-filters");if(tableFiltersEl){tableFiltersEl.replaceWith(Filters({id:id,filterRegistry:TableFilterRegistry,filters:filters,onChange:filters=>document.querySelector('form.search-form input[name="include_filters"]').value=base64_json_encode(filters)}))}})}})(jQuery);
  • groundhogg/trunk/assets/js/admin/funnels/simulator.js

    r3269144 r3476690  
    223223          ToolTip('Change contact', 'top'),
    224224        ]),
     225        Button({
     226          id       : `view-profile-${ item.ID }`,
     227          className: 'gh-button secondary text icon',
     228          onClick  : ()=>{
     229            window.open( Groundhogg.stores.contacts.get(State.contactId).admin, '_blank' )
     230          },
     231        }, [
     232          Dashicon('external'),
     233          ToolTip('View profile', 'top'),
     234        ]),
    225235      ]),
    226236    }) : Button({
  • groundhogg/trunk/assets/js/admin/funnels/simulator.min.js

    r3269144 r3476690  
    1 (()=>{const{Pg,Div,ItemPicker,Button,Span,Toggle,Label,Dashicon,ToolTip,ProgressBar}=MakeEl;const{icons}=Groundhogg.element;const{ajax}=Groundhogg.api;const{__,sprintf,_x,_n}=wp.i18n;const State=Groundhogg.createState({flow:[],options:[],index:0,current:null,simulating:false,scrollLog:true,contactId:null,dry:true});let contactId=localStorage.getItem("gh-simulate-contact-id");if(contactId){State.set({contactId:parseInt(contactId)})}else{Groundhogg.stores.contacts.fetchItems({users_include:[Groundhogg.user.getCurrentUser().ID]}).then(items=>{if(items.length){State.set({contactId:items[0].ID})}})}const startTimeline=()=>{const interval=setInterval(()=>{if(State.index>=State.flow.length){clearInterval(interval);State.set({simulating:false});Groundhogg.stores.contacts.fetchItem(State.contactId).then(morph);morph();return}if(Number.isInteger(State.flow[State.index])){focusStep(State.flow[State.index])}State.set({index:State.index+1});morph()},500)};const simulate=()=>{State.set({simulating:true});morph();ajax({action:"gh_flow_simulate",from:State.current,contact:State.contactId,dry:State.dry}).then(r=>{State.set({flow:[...State.flow,...r.flow],options:r.options});startTimeline()}).catch(err=>{Groundhogg.element.errorDialog({message:err.message})})};const SimStep=step=>{return Div({id:`flow-log-${step.ID}`,className:["flow-log-item",step.data.step_group,step.data.step_type].join(" "),onClick:e=>{focusStep(step.ID)}},[Div({className:"display-flex gap-10"},[Groundhogg.rawStepTypes[step.data.step_type].svg,Div({className:"display-flex column"},[Span({className:"step-title"},StepTitle(step)),Span({className:"step-name"},Groundhogg.rawStepTypes[step.data.step_type].name)])])])};const StepTitle=step=>{return Span({className:"step-title"},document.querySelector(`#step-${step.ID} .step-title`).innerHTML)};const ContinueStep=step=>Div({id:`flow-sim-continue-from-${step.ID}`,className:"flow-continue-item",onClick:e=>{State.set({current:step.ID});State.flow.push("Trigger selected...");simulate()}},SimStep(step));const focusStep=id=>document.getElementById(`step-${id}`).scrollIntoView({behavior:"smooth",block:"center",inline:"center"});const stepToOpt=step=>{return{id:step.ID,text:Span({className:`gh-text ${step.data.step_group}`},step.data.step_title)}};const handleChangeContact=()=>Groundhogg.components.selectContactModal({onSelect:contact=>{localStorage.setItem("gh-simulate-contact-id",contact.ID);State.set({contactId:contact.ID});morph()}});const ContactPreview=()=>Div({className:"gh-panel outlined"},[State.contactId?Groundhogg.components.ContactListItem(Groundhogg.stores.contacts.get(State.contactId),{extra:item=>Div({className:"display-flex flex-end"},[Button({id:`contact-edit-${item.ID}`,className:"gh-button secondary text icon",onClick:e=>{Groundhogg.components.quickEditContactModal({contact:item,onEdit:item=>{Groundhogg.element.dialog({message:__("Contact updated!","groundhogg")});morph()}})}},[Dashicon("edit"),ToolTip("Quick edit","top")]),Button({id:`contact-swap-${item.ID}`,className:"gh-button secondary text icon",onClick:handleChangeContact},[Dashicon("id"),ToolTip("Change contact","top")])])}):Button({id:"select-contact-for-simulator",onClick:handleChangeContact},"Select a contact")]);const FlowSimulator=()=>{if(State.contactId&&!Groundhogg.stores.contacts.has(State.contactId)){Groundhogg.stores.contacts.maybeFetchItem(State.contactId).then(morph);return Div({id:"flow-simulator"},[Div({className:"skeleton-loading"})])}return Div({id:"flow-simulator"},[Div({className:"start-from display-flex column gap-10"},[ContactPreview(),Div({className:"display-flex gap-5"},[Toggle({id:"simulate-dry-run",checked:State.dry,onChange:e=>State.set({dry:e.target.checked})}),Label({for:"simulate-dry-run"},"Dry run")]),Div({className:"display-flex gap-10"},[ItemPicker({id:"simulate-select-step",noneSelected:"Select a step to start from...",multiple:false,selected:State.current?[stepToOpt(Funnel.getStep(State.current))]:[],fetchOptions:async search=>{let regex=new RegExp(search,"i");return Funnel.steps.filter(item=>item.data.step_title.match(regex)).map(stepToOpt)},onChange:item=>{State.set({current:item.id});focusStep(State.current)}}),Button({id:"run-simulator",className:"gh-button primary",disabled:State.simulating||!State.contactId,onClick:e=>{State.set({index:0,flow:[],options:[]});simulate()}},State.simulating?Span({className:"gh-spinner"}):"Run Simulation")])]),Div({id:"flow-simulator-log",onScroll:e=>{State.set({scrollLog:isAtBottom(e.target)})}},[...State.flow.slice(0,State.index).map(item=>{if(Number.isInteger(item)){let step=Funnel.getStep(item);if(step){return SimStep(Funnel.getStep(item))}}return Pg({className:"flow-log-item"},item)}),State.index>=State.flow.length&&State.options.length&&State.flow.length?Div({},[Pg({},"Select a trigger to continue..."),Div({className:"display-flex gap-20"},[...State.options.map(id=>ContinueStep(Funnel.getStep(id)))])]):null])])};function isAtBottom(el){return el.scrollTop+el.clientHeight>=el.scrollHeight-1}const morph=()=>{morphdom(document.querySelector("#flow-simulator"),FlowSimulator(),{onNodeAdded:el=>{if(el.matches&&el.matches(".flow-log-item")&&State.simulating&&State.scrollLog){scrollLogToBottom()}}})};const scrollLogToBottom=()=>{let log=document.getElementById("flow-simulator-log");log.scrollTo({top:log.scrollHeight,behavior:"smooth"})};document.addEventListener("DOMContentLoaded",morph);Groundhogg.simulator={state:State,FlowSimulator:FlowSimulator,morph:morph}})();
     1(()=>{const{Pg,Div,ItemPicker,Button,Span,Toggle,Label,Dashicon,ToolTip,ProgressBar}=MakeEl;const{icons}=Groundhogg.element;const{ajax}=Groundhogg.api;const{__,sprintf,_x,_n}=wp.i18n;const State=Groundhogg.createState({flow:[],options:[],index:0,current:null,simulating:false,scrollLog:true,contactId:null,dry:true});let contactId=localStorage.getItem("gh-simulate-contact-id");if(contactId){State.set({contactId:parseInt(contactId)})}else{Groundhogg.stores.contacts.fetchItems({users_include:[Groundhogg.user.getCurrentUser().ID]}).then(items=>{if(items.length){State.set({contactId:items[0].ID})}})}const startTimeline=()=>{const interval=setInterval(()=>{if(State.index>=State.flow.length){clearInterval(interval);State.set({simulating:false});Groundhogg.stores.contacts.fetchItem(State.contactId).then(morph);morph();return}if(Number.isInteger(State.flow[State.index])){focusStep(State.flow[State.index])}State.set({index:State.index+1});morph()},500)};const simulate=()=>{State.set({simulating:true});morph();ajax({action:"gh_flow_simulate",from:State.current,contact:State.contactId,dry:State.dry}).then(r=>{State.set({flow:[...State.flow,...r.flow],options:r.options});startTimeline()}).catch(err=>{Groundhogg.element.errorDialog({message:err.message})})};const SimStep=step=>{return Div({id:`flow-log-${step.ID}`,className:["flow-log-item",step.data.step_group,step.data.step_type].join(" "),onClick:e=>{focusStep(step.ID)}},[Div({className:"display-flex gap-10"},[Groundhogg.rawStepTypes[step.data.step_type].svg,Div({className:"display-flex column"},[Span({className:"step-title"},StepTitle(step)),Span({className:"step-name"},Groundhogg.rawStepTypes[step.data.step_type].name)])])])};const StepTitle=step=>{return Span({className:"step-title"},document.querySelector(`#step-${step.ID} .step-title`).innerHTML)};const ContinueStep=step=>Div({id:`flow-sim-continue-from-${step.ID}`,className:"flow-continue-item",onClick:e=>{State.set({current:step.ID});State.flow.push("Trigger selected...");simulate()}},SimStep(step));const focusStep=id=>document.getElementById(`step-${id}`).scrollIntoView({behavior:"smooth",block:"center",inline:"center"});const stepToOpt=step=>{return{id:step.ID,text:Span({className:`gh-text ${step.data.step_group}`},step.data.step_title)}};const handleChangeContact=()=>Groundhogg.components.selectContactModal({onSelect:contact=>{localStorage.setItem("gh-simulate-contact-id",contact.ID);State.set({contactId:contact.ID});morph()}});const ContactPreview=()=>Div({className:"gh-panel outlined"},[State.contactId?Groundhogg.components.ContactListItem(Groundhogg.stores.contacts.get(State.contactId),{extra:item=>Div({className:"display-flex flex-end"},[Button({id:`contact-edit-${item.ID}`,className:"gh-button secondary text icon",onClick:e=>{Groundhogg.components.quickEditContactModal({contact:item,onEdit:item=>{Groundhogg.element.dialog({message:__("Contact updated!","groundhogg")});morph()}})}},[Dashicon("edit"),ToolTip("Quick edit","top")]),Button({id:`contact-swap-${item.ID}`,className:"gh-button secondary text icon",onClick:handleChangeContact},[Dashicon("id"),ToolTip("Change contact","top")]),Button({id:`view-profile-${item.ID}`,className:"gh-button secondary text icon",onClick:()=>{window.open(Groundhogg.stores.contacts.get(State.contactId).admin,"_blank")}},[Dashicon("external"),ToolTip("View profile","top")])])}):Button({id:"select-contact-for-simulator",onClick:handleChangeContact},"Select a contact")]);const FlowSimulator=()=>{if(State.contactId&&!Groundhogg.stores.contacts.has(State.contactId)){Groundhogg.stores.contacts.maybeFetchItem(State.contactId).then(morph);return Div({id:"flow-simulator"},[Div({className:"skeleton-loading"})])}return Div({id:"flow-simulator"},[Div({className:"start-from display-flex column gap-10"},[ContactPreview(),Div({className:"display-flex gap-5"},[Toggle({id:"simulate-dry-run",checked:State.dry,onChange:e=>State.set({dry:e.target.checked})}),Label({for:"simulate-dry-run"},"Dry run")]),Div({className:"display-flex gap-10"},[ItemPicker({id:"simulate-select-step",noneSelected:"Select a step to start from...",multiple:false,selected:State.current?[stepToOpt(Funnel.getStep(State.current))]:[],fetchOptions:async search=>{let regex=new RegExp(search,"i");return Funnel.steps.filter(item=>item.data.step_title.match(regex)).map(stepToOpt)},onChange:item=>{State.set({current:item.id});focusStep(State.current)}}),Button({id:"run-simulator",className:"gh-button primary",disabled:State.simulating||!State.contactId,onClick:e=>{State.set({index:0,flow:[],options:[]});simulate()}},State.simulating?Span({className:"gh-spinner"}):"Run Simulation")])]),Div({id:"flow-simulator-log",onScroll:e=>{State.set({scrollLog:isAtBottom(e.target)})}},[...State.flow.slice(0,State.index).map(item=>{if(Number.isInteger(item)){let step=Funnel.getStep(item);if(step){return SimStep(Funnel.getStep(item))}}return Pg({className:"flow-log-item"},item)}),State.index>=State.flow.length&&State.options.length&&State.flow.length?Div({},[Pg({},"Select a trigger to continue..."),Div({className:"display-flex gap-20"},[...State.options.map(id=>ContinueStep(Funnel.getStep(id)))])]):null])])};function isAtBottom(el){return el.scrollTop+el.clientHeight>=el.scrollHeight-1}const morph=()=>{morphdom(document.querySelector("#flow-simulator"),FlowSimulator(),{onNodeAdded:el=>{if(el.matches&&el.matches(".flow-log-item")&&State.simulating&&State.scrollLog){scrollLogToBottom()}}})};const scrollLogToBottom=()=>{let log=document.getElementById("flow-simulator-log");log.scrollTo({top:log.scrollHeight,behavior:"smooth"})};document.addEventListener("DOMContentLoaded",morph);Groundhogg.simulator={state:State,FlowSimulator:FlowSimulator,morph:morph}})();
  • groundhogg/trunk/assets/js/admin/reports/reporting.js

    r3264477 r3476690  
    247247        }))
    248248
     249      },
     250
     251      setOtherDataAndRefresh: function (data) {
     252        this.other = {
     253          ...this.other,
     254          ...data,
     255        }
     256        this.refresh(this.calendar)
    249257      },
    250258
  • groundhogg/trunk/assets/js/admin/reports/reporting.min.js

    r3264477 r3476690  
    1 (function($,nonces){const{sprintf,__,_x,_n}=wp.i18n;const{emails:EmailsStore,campaigns:CampaignsStore}=Groundhogg.stores;const{ItemPicker,Table,Tr,Td,Th,THead,TBody,Fragment,Button,Div,Span}=MakeEl;const{loadingModal,adminPageURL}=Groundhogg.element;const{base64_json_encode}=Groundhogg.functions;const openInContactsView=filters=>{window.open(adminPageURL("gh_contacts",{filters:base64_json_encode(filters)}),"_blank")};const ReportTable=(id,report)=>{let{label,data,no_data:no_data="",per_page:per_page=10,orderby:orderby=0}=report;if(!Array.isArray(label)){label=Object.values(label)}let sortable=data.length&&data[0].orderby;const State=Groundhogg.createState({per_page:per_page,orderby:orderby,orderby2:0,order:"DESC",page:0});const compareRows=(a,b,k=State.orderby)=>{if(!sortable||!a.orderby){return 0}let av=a.orderby[k];let bv=b.orderby[k];if(av===bv&&k!==State.orderby2){return compareRows(a,b,State.orderby2)}if(State.order==="ASC"){return av-bv}return bv-av};const getData=()=>data.sort(compareRows).slice(State.per_page*State.page,State.per_page*State.page+State.per_page);const TableBody=()=>TBody({},getData().map(({orderby:orderby={},cellClasses:cellClasses=[],...row})=>Tr({},Object.keys(row).map((k,i)=>{return Td({dataColname:k,className:`${cellClasses[i]??""}`},`${row[k]}`)}))));return Div({id:`report-${id}`},morph=>Fragment([Div({className:"table-scroll"},Table({className:"groundhogg-report-table"},[THead({},Tr({},label.map((l,i)=>Th({id:`order-${i}`,className:`${State.orderby===i||State.orderby2===i?"sorted":""} ${State.order==="ASC"?"asc":"desc"}`,onClick:e=>{if(!sortable){return}if(State.orderby===i){State.set({order:State.order==="ASC"?"DESC":"ASC"})}else{State.set({orderby:i,orderby2:State.orderby,order:"DESC"})}morph()}},Div({className:`display-flex ${i===0?"flex-start":i===label.length-1?"flex-end":"center"}`},[Span({className:"column-name"},l),sortable?Span({},[Span({className:"sorting-indicator asc"}),Span({className:"sorting-indicator desc"})]):null]))))),TableBody()])),data.length>State.per_page?Div({style:{padding:"10px"},className:"display-flex gap-10 flex-end"},[State.page>0?Button({id:`report-${id}-prev`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page-1});morph()}},"Prev"):null,(State.page+1)*State.per_page<data.length?Button({id:`report-${id}-next`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page+1});morph()}},"Next"):null]):null]))};if(typeof GroundhoggReporting!=="undefined"){const reporting=GroundhoggReporting;$.extend(reporting||{},{data:{},calendar:null,charts:{},init:function(){this.initCalendar();this.initFunnels();this.initCountry();this.initBroadcast();this.initCampaignFilter()},async initCampaignFilter(){let el=document.getElementById("report-campaign-filter");if(!el){return}let campaignId=this.other.campaign;if(campaignId&&!CampaignsStore.has(campaignId)){await CampaignsStore.fetchItem(campaignId)}el.append(ItemPicker({id:"report-campaign",noneSelected:__("Filter by campaign...","groundhogg"),multiple:false,selected:campaignId?(({ID,data})=>({id:ID,text:data.name}))(CampaignsStore.get(campaignId)):[],fetchOptions:async search=>{let campaigns=await CampaignsStore.fetchItems({search:search,limit:20});return campaigns.map(({ID,data})=>({id:ID,text:data.name}))},onChange:item=>{if(!item){this.other.campaign=null}else{this.other.campaign=item.id}this.refresh(this.calendar)}}))},initCalendar:function(){var self=this;this.calendar=new Calendar({element:$("#groundhogg-datepicker"),presets:[{label:"Last 30 days",start:moment().subtract(29,"days"),end:moment()},{label:"This month",start:moment().startOf("month"),end:moment().endOf("month")},{label:"Last month",start:moment().subtract(1,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"Last 7 days",start:moment().subtract(6,"days"),end:moment()},{label:"Last 3 months",start:moment().subtract(3,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"This year",start:moment().startOf("year"),end:moment().endOf("year")}],format:{preset:GroundhoggReporting.date_format},earliest_date:"January 1, 2017",latest_date:moment(),start_date:self.dates.start_date,end_date:self.dates.end_date,callback:function(){self.refresh(this)}});this.calendar.calendarSaveDates()},initFunnels:function(){var self=this;$("#funnel-id").change(function(){self.refresh(self.calendar)})},initBroadcast:function(){var self=this;$("#broadcast-id").change(function(){self.refresh(self.calendar)})},initCountry:function(){var self=this;$("#country").change(function(){self.refresh(self.calendar)})},refresh:function(calendar){var self=this;let{close}=loadingModal();var start=calendar.start_date.format("YYYY-MM-DD"),end=calendar.end_date.format("YYYY-MM-DD");$.ajax({type:"post",url:ajaxurl,dataType:"json",data:{action:"groundhogg_refresh_dashboard_reports",reports:self.reports,start:start,end:end,data:{...reporting.other}},success:function(json){self.data=json.data.reports;self.renderReports();close();$(".wrap").removeClass("blurred");window.dispatchEvent(new Event("resize"))},failure:function(response){alert("Unable to retrieve data...")}})},get_other_data:function(){var self=this;var data={};$(".post-data").each(function(i){var $this=$(this);var name=$this.attr("name");if(name==="funnel"){name="funnel_id"}data[name]=$this.val()});return data},renderReports:function(){for(var i=0;i<this.reports.length;i++){var report_id=this.reports[i];var report_data=this.data[report_id];this.renderReport(report_id,report_data)}},renderReport:function(report_id,report_data){var $report=$("#"+report_id);if(!$report.length){return}var type=report_data.type;switch(type){case"quick_stat":this.renderQuickStatReport($report,report_data);break;case"chart":this.renderChartReport($report,report_data.chart,report_id);break;case"table":this.renderTable($report,report_data,report_id);break;case"funnel":this.renderFunnelFlowReport(report_data);break;case"funnel_breakdown":this.renderFunnelBreakdownReport($report,report_data);break}},renderFunnelBreakdownReport($report,report_data){const{report}=report_data;$report.html("");let reportEl=$report[0];let labelsEl=MakeEl.Div({className:"funnel-labels"},[...report.map(({link,labels,percentage:percentage=null})=>{return MakeEl.Fragment([MakeEl.Div({className:"funnel-stage-label"},MakeEl.Span({},Groundhogg.element.orList(labels)))])})]);let minWidth=10;let funnelEl=MakeEl.Div({className:"funnel-stages"},[...report.map(({link,width,complete,percentage:percentage=null})=>{let percentEl=null;if(percentage!==null){percentEl=MakeEl.Div({className:"stage-percentage",style:{width:`${Math.max(width,minWidth)}%`}},percentage==width?`${percentage}%`:`${percentage}% (${width}%)`)}return MakeEl.Fragment([percentEl,MakeEl.Div({className:"funnel-stage-layer",style:{width:`${Math.max(width,minWidth)}%`},onClick:e=>{let a=e.currentTarget.querySelector("a");if(a){a.click()}}},[link])])})]);reportEl.append(labelsEl);reportEl.append(funnelEl);applyClipPath(funnelEl)},renderFunnelFlowReport(report_data){const{stepData:stepData=[]}=report_data;stepData.forEach(({step:step=0,complete:complete="",waiting:waiting=""})=>{step=document.getElementById(`step-${step}`);if(!step){return}let completeEl=step.querySelector(".complete");let waitingEl=step.querySelector(".waiting");if(completeEl){completeEl.innerHTML=complete}if(waitingEl){waitingEl.innerHTML=waiting;if(waiting==="0"){waitingEl.closest(".stat-wrap").classList.add("invisible")}else{waitingEl.closest(".stat-wrap").classList.remove("invisible")}}});Groundhogg.drawLogicLines()},renderQuickStatReport:function($report,report_data){$report.find(".groundhogg-quick-stat-number").html(report_data.number);$report.find(".groundhogg-quick-stat-previous").removeClass("green red").addClass(report_data.compare.arrow.color);$report.find(".groundhogg-quick-stat-compare").html(report_data.compare.text);$report.find(".groundhogg-quick-stat-arrow").removeClass("up down").addClass(report_data.compare.arrow.direction);$report.find(".groundhogg-quick-stat-prev-percent").html(report_data.compare.percent)},renderChartReport:function($report,report_data,report_id){if(report_data.data.labels&&report_data.data.labels.length===0){$report.closest(".gh-donut-chart-wrap").html(report_data.no_data);return}if(this.charts[report_id]){this.charts[report_id].destroy()}var ctx=$report[0].getContext("2d");switch(report_id){case"chart_unsub_reasons":report_data.options.onClick=(e,arr)=>{let index=arr[0]._index;let{reason}=report_data.data.rawResults[index];openInContactsView([[{type:"unsubscribed",reasons:[reason],date_range:"between",before:this.calendar.end_date.format("YYYY-MM-DD"),after:this.calendar.start_date.format("YYYY-MM-DD")}]])};break}var chart=new Chart(ctx,report_data);this.charts[report_id]=chart;var draw_line=Chart.controllers.line.prototype.draw;Chart.helpers.extend(Chart.controllers.line.prototype,{draw:function(){draw_line.apply(this,arguments);if(this.chart.tooltip._active&&this.chart.tooltip._active.length){var ap=this.chart.tooltip._active[0];var ctx=this.chart.ctx;var x=ap.tooltipPosition().x;var topy=this.chart.scales["y-axis-0"].top;var bottomy=this.chart.scales["y-axis-0"].bottom;ctx.save();ctx.beginPath();ctx.moveTo(x,topy);ctx.lineTo(x,bottomy);ctx.lineWidth=1;ctx.strokeStyle="#727272";ctx.setLineDash([10,10]);ctx.stroke();ctx.restore()}}});Chart.plugins.register({afterDraw:function(chart){if(chart.data.datasets.length===0){var ctx=chart.chart.ctx;var width=chart.chart.width;var height=chart.chart.height;chart.clear();ctx.save();ctx.textAlign="center";ctx.textBaseline="middle";ctx.font="16px normal 'Helvetica Nueue'";ctx.fillText("No data to display",width/2,height/2);ctx.restore()}}})},renderTable:function($report,report_data,id){let{data,no_data:no_data=""}=report_data;if(!data.length){$report.html(no_data);return}$report.html(ReportTable(id,report_data))}})}function applyClipPath(element){const boxes=element.children;for(let i=0;i<boxes.length-1;i++){const current=boxes[i];const next=boxes[i+1];const currentWidth=current.offsetWidth;const nextWidth=next.offsetWidth;const leftDiff=(currentWidth-nextWidth)/2;const rightDiff=(currentWidth-nextWidth)/2;current.style.clipPath=`polygon(
     1(function($,nonces){const{sprintf,__,_x,_n}=wp.i18n;const{emails:EmailsStore,campaigns:CampaignsStore}=Groundhogg.stores;const{ItemPicker,Table,Tr,Td,Th,THead,TBody,Fragment,Button,Div,Span}=MakeEl;const{loadingModal,adminPageURL}=Groundhogg.element;const{base64_json_encode}=Groundhogg.functions;const openInContactsView=filters=>{window.open(adminPageURL("gh_contacts",{filters:base64_json_encode(filters)}),"_blank")};const ReportTable=(id,report)=>{let{label,data,no_data="",per_page=10,orderby=0}=report;if(!Array.isArray(label)){label=Object.values(label)}let sortable=data.length&&data[0].orderby;const State=Groundhogg.createState({per_page:per_page,orderby:orderby,orderby2:0,order:"DESC",page:0});const compareRows=(a,b,k=State.orderby)=>{if(!sortable||!a.orderby){return 0}let av=a.orderby[k];let bv=b.orderby[k];if(av===bv&&k!==State.orderby2){return compareRows(a,b,State.orderby2)}if(State.order==="ASC"){return av-bv}return bv-av};const getData=()=>data.sort(compareRows).slice(State.per_page*State.page,State.per_page*State.page+State.per_page);const TableBody=()=>TBody({},getData().map(({orderby={},cellClasses=[],...row})=>Tr({},Object.keys(row).map((k,i)=>{return Td({dataColname:k,className:`${cellClasses[i]??""}`},`${row[k]}`)}))));return Div({id:`report-${id}`},morph=>Fragment([Div({className:"table-scroll"},Table({className:"groundhogg-report-table"},[THead({},Tr({},label.map((l,i)=>Th({id:`order-${i}`,className:`${State.orderby===i||State.orderby2===i?"sorted":""} ${State.order==="ASC"?"asc":"desc"}`,onClick:e=>{if(!sortable){return}if(State.orderby===i){State.set({order:State.order==="ASC"?"DESC":"ASC"})}else{State.set({orderby:i,orderby2:State.orderby,order:"DESC"})}morph()}},Div({className:`display-flex ${i===0?"flex-start":i===label.length-1?"flex-end":"center"}`},[Span({className:"column-name"},l),sortable?Span({},[Span({className:"sorting-indicator asc"}),Span({className:"sorting-indicator desc"})]):null]))))),TableBody()])),data.length>State.per_page?Div({style:{padding:"10px"},className:"display-flex gap-10 flex-end"},[State.page>0?Button({id:`report-${id}-prev`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page-1});morph()}},"Prev"):null,(State.page+1)*State.per_page<data.length?Button({id:`report-${id}-next`,className:"gh-button secondary",onClick:e=>{State.set({page:State.page+1});morph()}},"Next"):null]):null]))};if(typeof GroundhoggReporting!=="undefined"){const reporting=GroundhoggReporting;$.extend(reporting||{},{data:{},calendar:null,charts:{},init:function(){this.initCalendar();this.initFunnels();this.initCountry();this.initBroadcast();this.initCampaignFilter()},async initCampaignFilter(){let el=document.getElementById("report-campaign-filter");if(!el){return}let campaignId=this.other.campaign;if(campaignId&&!CampaignsStore.has(campaignId)){await CampaignsStore.fetchItem(campaignId)}el.append(ItemPicker({id:"report-campaign",noneSelected:__("Filter by campaign...","groundhogg"),multiple:false,selected:campaignId?(({ID,data})=>({id:ID,text:data.name}))(CampaignsStore.get(campaignId)):[],fetchOptions:async search=>{let campaigns=await CampaignsStore.fetchItems({search:search,limit:20});return campaigns.map(({ID,data})=>({id:ID,text:data.name}))},onChange:item=>{if(!item){this.other.campaign=null}else{this.other.campaign=item.id}this.refresh(this.calendar)}}))},setOtherDataAndRefresh:function(data){this.other={...this.other,...data};this.refresh(this.calendar)},initCalendar:function(){var self=this;this.calendar=new Calendar({element:$("#groundhogg-datepicker"),presets:[{label:"Last 30 days",start:moment().subtract(29,"days"),end:moment()},{label:"This month",start:moment().startOf("month"),end:moment().endOf("month")},{label:"Last month",start:moment().subtract(1,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"Last 7 days",start:moment().subtract(6,"days"),end:moment()},{label:"Last 3 months",start:moment().subtract(3,"month").startOf("month"),end:moment().subtract(1,"month").endOf("month")},{label:"This year",start:moment().startOf("year"),end:moment().endOf("year")}],format:{preset:GroundhoggReporting.date_format},earliest_date:"January 1, 2017",latest_date:moment(),start_date:self.dates.start_date,end_date:self.dates.end_date,callback:function(){self.refresh(this)}});this.calendar.calendarSaveDates()},initFunnels:function(){var self=this;$("#funnel-id").change(function(){self.refresh(self.calendar)})},initBroadcast:function(){var self=this;$("#broadcast-id").change(function(){self.refresh(self.calendar)})},initCountry:function(){var self=this;$("#country").change(function(){self.refresh(self.calendar)})},refresh:function(calendar){var self=this;let{close}=loadingModal();var start=calendar.start_date.format("YYYY-MM-DD"),end=calendar.end_date.format("YYYY-MM-DD");$.ajax({type:"post",url:ajaxurl,dataType:"json",data:{action:"groundhogg_refresh_dashboard_reports",reports:self.reports,start:start,end:end,data:{...reporting.other}},success:function(json){self.data=json.data.reports;self.renderReports();close();$(".wrap").removeClass("blurred");window.dispatchEvent(new Event("resize"))},failure:function(response){alert("Unable to retrieve data...")}})},get_other_data:function(){var self=this;var data={};$(".post-data").each(function(i){var $this=$(this);var name=$this.attr("name");if(name==="funnel"){name="funnel_id"}data[name]=$this.val()});return data},renderReports:function(){for(var i=0;i<this.reports.length;i++){var report_id=this.reports[i];var report_data=this.data[report_id];this.renderReport(report_id,report_data)}},renderReport:function(report_id,report_data){var $report=$("#"+report_id);if(!$report.length){return}var type=report_data.type;switch(type){case"quick_stat":this.renderQuickStatReport($report,report_data);break;case"chart":this.renderChartReport($report,report_data.chart,report_id);break;case"table":this.renderTable($report,report_data,report_id);break;case"funnel":this.renderFunnelFlowReport(report_data);break;case"funnel_breakdown":this.renderFunnelBreakdownReport($report,report_data);break}},renderFunnelBreakdownReport($report,report_data){const{report}=report_data;$report.html("");let reportEl=$report[0];let labelsEl=MakeEl.Div({className:"funnel-labels"},[...report.map(({link,labels,percentage=null})=>{return MakeEl.Fragment([MakeEl.Div({className:"funnel-stage-label"},MakeEl.Span({},Groundhogg.element.orList(labels)))])})]);let minWidth=10;let funnelEl=MakeEl.Div({className:"funnel-stages"},[...report.map(({link,width,complete,percentage=null})=>{let percentEl=null;if(percentage!==null){percentEl=MakeEl.Div({className:"stage-percentage",style:{width:`${Math.max(width,minWidth)}%`}},percentage==width?`${percentage}%`:`${percentage}% (${width}%)`)}return MakeEl.Fragment([percentEl,MakeEl.Div({className:"funnel-stage-layer",style:{width:`${Math.max(width,minWidth)}%`},onClick:e=>{let a=e.currentTarget.querySelector("a");if(a){a.click()}}},[link])])})]);reportEl.append(labelsEl);reportEl.append(funnelEl);applyClipPath(funnelEl)},renderFunnelFlowReport(report_data){const{stepData=[]}=report_data;stepData.forEach(({step=0,complete="",waiting=""})=>{step=document.getElementById(`step-${step}`);if(!step){return}let completeEl=step.querySelector(".complete");let waitingEl=step.querySelector(".waiting");if(completeEl){completeEl.innerHTML=complete}if(waitingEl){waitingEl.innerHTML=waiting;if(waiting==="0"){waitingEl.closest(".stat-wrap").classList.add("invisible")}else{waitingEl.closest(".stat-wrap").classList.remove("invisible")}}});Groundhogg.drawLogicLines()},renderQuickStatReport:function($report,report_data){$report.find(".groundhogg-quick-stat-number").html(report_data.number);$report.find(".groundhogg-quick-stat-previous").removeClass("green red").addClass(report_data.compare.arrow.color);$report.find(".groundhogg-quick-stat-compare").html(report_data.compare.text);$report.find(".groundhogg-quick-stat-arrow").removeClass("up down").addClass(report_data.compare.arrow.direction);$report.find(".groundhogg-quick-stat-prev-percent").html(report_data.compare.percent)},renderChartReport:function($report,report_data,report_id){if(report_data.data.labels&&report_data.data.labels.length===0){$report.closest(".gh-donut-chart-wrap").html(report_data.no_data);return}if(this.charts[report_id]){this.charts[report_id].destroy()}var ctx=$report[0].getContext("2d");switch(report_id){case"chart_unsub_reasons":report_data.options.onClick=(e,arr)=>{let index=arr[0]._index;let{reason}=report_data.data.rawResults[index];openInContactsView([[{type:"unsubscribed",reasons:[reason],date_range:"between",before:this.calendar.end_date.format("YYYY-MM-DD"),after:this.calendar.start_date.format("YYYY-MM-DD")}]])};break}var chart=new Chart(ctx,report_data);this.charts[report_id]=chart;var draw_line=Chart.controllers.line.prototype.draw;Chart.helpers.extend(Chart.controllers.line.prototype,{draw:function(){draw_line.apply(this,arguments);if(this.chart.tooltip._active&&this.chart.tooltip._active.length){var ap=this.chart.tooltip._active[0];var ctx=this.chart.ctx;var x=ap.tooltipPosition().x;var topy=this.chart.scales["y-axis-0"].top;var bottomy=this.chart.scales["y-axis-0"].bottom;ctx.save();ctx.beginPath();ctx.moveTo(x,topy);ctx.lineTo(x,bottomy);ctx.lineWidth=1;ctx.strokeStyle="#727272";ctx.setLineDash([10,10]);ctx.stroke();ctx.restore()}}});Chart.plugins.register({afterDraw:function(chart){if(chart.data.datasets.length===0){var ctx=chart.chart.ctx;var width=chart.chart.width;var height=chart.chart.height;chart.clear();ctx.save();ctx.textAlign="center";ctx.textBaseline="middle";ctx.font="16px normal 'Helvetica Nueue'";ctx.fillText("No data to display",width/2,height/2);ctx.restore()}}})},renderTable:function($report,report_data,id){let{data,no_data=""}=report_data;if(!data.length){$report.html(no_data);return}$report.html(ReportTable(id,report_data))}})}function applyClipPath(element){const boxes=element.children;for(let i=0;i<boxes.length-1;i++){const current=boxes[i];const next=boxes[i+1];const currentWidth=current.offsetWidth;const nextWidth=next.offsetWidth;const leftDiff=(currentWidth-nextWidth)/2;const rightDiff=(currentWidth-nextWidth)/2;current.style.clipPath=`polygon(
    22                    0% 0%,
    33                    100% 0%,
  • groundhogg/trunk/db/query/filters.php

    r3400645 r3476690  
    179179                $before->modify( 'tomorrow 23:59:59' );
    180180                break;
     181
    181182            case 'this_week':
    182 
    183183                $startEnd = get_weekstartend( $after->ymdhis() );
    184184                $after->setTimestamp( $startEnd['start'] );
    185185                $before->setTimestamp( $startEnd['end'] );
    186 
    187186                break;
    188187            case 'last_week':
    189 
    190188                $after->modify( '7 days ago' );
    191189                $startEnd = get_weekstartend( $after->ymdhis() );
    192190                $after->setTimestamp( $startEnd['start'] );
    193191                $before->setTimestamp( $startEnd['end'] );
    194 
    195                 break;
    196 
     192                break;
     193            case 'next_week':
     194                $after->modify( '+7 days' );
     195                $startEnd = get_weekstartend( $after->ymdhis() );
     196                $after->setTimestamp( $startEnd['start'] );
     197                $before->setTimestamp( $startEnd['end'] );
     198                break;
    197199            case 'this_month':
    198200                $after->modify( 'first day of this month 00:00:00' );
     
    203205                $before->modify( 'last day of last month 23:59:59' );
    204206                break;
     207            case 'next_month':
     208                $after->modify( 'first day of next month 00:00:00' );
     209                $before->modify( 'last day of next month 23:59:59' );
     210                break;
     211            case 'this_quarter':
     212                $after->toStartOfQuarter();
     213                $before->toEndOfQuarter();
     214                break;
     215            case 'last_quarter':
     216                $after->modify('-3 months')->toStartOfQuarter();
     217                $before->modify('-3 months')->toEndOfQuarter();
     218                break;
     219            case 'next_quarter':
     220                $after->modify( '+3 months')->toStartOfQuarter();
     221                $before->modify( '+3 months')->toEndOfQuarter();
     222                break;
    205223            case 'this_year':
    206224                $after->modify( 'first day of January this year 00:00:00' );
    207225                $before->modify( 'last day of December this year 23:59:59' );
     226                break;
     227            case 'last_year':
     228                $after->modify('-1 year')->modify( 'first day of January this year 00:00:00' );
     229                $before->modify('-1 year')->modify( 'last day of December this year 23:59:59' );
     230                break;
     231            case 'next_year':
     232                $after->modify('+1 year')->modify( 'first day of January this year 00:00:00' );
     233                $before->modify('+1 year')->modify( 'last day of December this year 23:59:59' );
    208234                break;
    209235            case '24_hours':
  • groundhogg/trunk/groundhogg.php

    r3464589 r3476690  
    44 * Plugin URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash
    55 * Description: CRM and marketing automation for WordPress
    6  * Version: 4.3
     6 * Version: 4.3.1
    77 * Author: Groundhogg Inc.
    88 * Author URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash
     
    2525if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
    2626
    27 define( 'GROUNDHOGG_VERSION', '4.3' );
    28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.12' );
     27define( 'GROUNDHOGG_VERSION', '4.3.1' );
     28define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.3' );
    2929
    3030define( 'GROUNDHOGG__FILE__', __FILE__ );
  • groundhogg/trunk/includes/better-meta-compat.php

    r3422142 r3476690  
    6262            return sanitize_textarea_field( $value );
    6363        case 'number':
    64             return intval( $value );
     64            return number_format( (float) $value, $field['decimals'] ?? 0 );
    6565        case 'time':
    6666            return date( 'H:i:s', strtotime( $value ) );
  • groundhogg/trunk/includes/functions.php

    r3464589 r3476690  
    33namespace Groundhogg;
    44
     5use Groundhogg\Admin\Funnels\Simulator;
    56use Groundhogg\Classes\Activity;
    67use Groundhogg\Classes\Page_Visit;
     
    89158916 */
    89168917function add_event_args( $args = [] ) {
     8918
     8919    // special handling for the simulator
     8920    if ( Simulator::is_simulating() ){
     8921        Simulator::set_event_args( $args );
     8922        return true;
     8923    }
     8924
    89178925    if ( ! \Groundhogg\event_queue()::is_processing() ) {
    89188926        return false;
     
    89338941 */
    89348942function get_event_arg( string $arg, $default = false ) {
     8943
     8944    // special handling for the simulator
     8945    if ( Simulator::is_simulating() ){
     8946        return Simulator::get_event_arg( $arg, $default );
     8947    }
    89358948
    89368949    if ( ! \Groundhogg\event_queue()::is_processing() ) {
  • groundhogg/trunk/includes/properties.php

    r3343709 r3476690  
    9797            'order'   => 'absint',
    9898            'width'   => 'absint',
     99            'decimals' => 'absint',
    99100            'multiple' => 'boolval',
    100101            'options' => function ( $array ) {
  • groundhogg/trunk/includes/utils/date-time-helper.php

    r3422142 r3476690  
    116116    public function date_i18n() {
    117117        return $this->i18n( get_option( 'date_format' ) );
     118    }
     119
     120    /**
     121     * Move to the start of the quarter relative to the current time
     122     *
     123     * @return $this
     124     */
     125    public function toStartOfQuarter() {
     126
     127        $month = (int) $this->format('n');
     128        $quarter = (int) ceil($month / 3);
     129
     130        $start = (clone $this)->setDate(
     131            (int) $this->format('Y'),
     132            (($quarter - 1) * 3) + 1,
     133            1
     134        )->setTime(0, 0, 0);
     135
     136        $this->setTimestamp( $start->getTimestamp() );
     137
     138        return $this;
     139    }
     140
     141    /**
     142     * Move to the end of the quarter relative to the current time
     143     *
     144     * @throws \DateMalformedStringException
     145     * @return \DateTime|false
     146     */
     147    public function toEndOfQuarter() {
     148        return $this->toStartOfQuarter()
     149                    ->modify('+3 months')
     150                    ->modify('-1 second');
    118151    }
    119152
  • groundhogg/trunk/includes/utils/html.php

    r3344140 r3476690  
    17201720    }
    17211721
     1722    /**
     1723     * An easy tooltip helper
     1724     *
     1725     * @param  string  $text
     1726     * @param  string  $position
     1727     * @param $echo
     1728     *
     1729     * @return string
     1730     */
     1731    public function info_tooltip( string $text, string $position = 'top', $echo = false ) {
     1732        return $this->e( 'span', [ 'class' => 'dashicons dashicons-info' ], [
     1733            $this->e('span', [ 'class' => 'gh-tooltip ' . $position ], $text ),
     1734        ], '', $echo );
     1735    }
     1736
     1737    public function help_tooltip( string $text ) {
     1738
     1739    }
     1740
    17221741}
Note: See TracChangeset for help on using the changeset viewer.