/* global wpforms_tools_entries_export, ajaxurl, Choices */ /** * WPForms Entries Export function. * * @since 1.5.5 */ const WPFormsEntriesExport = window.WPFormsEntriesExport || ( function( document, window, $ ) { /** * Elements. * * @since 1.5.5 * * @type {Object} */ const el = { $form : $( '#wpforms-tools-entries-export' ), $selectForm : $( '#wpforms-tools-entries-export-selectform' ), $selectFormSpinner : $( '#wpforms-tools-entries-export-selectform-spinner' ), $selectFormMsg : $( '#wpforms-tools-entries-export-selectform-msg' ), $expOptions : $( '#wpforms-tools-entries-export-options' ), $fieldsCheckboxes : $( '#wpforms-tools-entries-export-options-fields-checkboxes' ), $paymentFieldsSection : $( '#wpforms-tools-entries-export-options-payment-fields' ), $paymentFieldsCheckboxes : $( '#wpforms-tools-entries-export-options-payment-fields-checkboxes' ), $dateSection : $( '#wpforms-tools-entries-export-options-date' ), $dateFlatpickr : $( '#wpforms-tools-entries-export-options-date-flatpickr' ), $searchSection : $( '#wpforms-tools-entries-export-options-search' ), $searchField : $( '#wpforms-tools-entries-export-options-search-field' ), $submitButton : $( '#wpforms-tools-entries-export-submit' ), $cancelButton : $( '#wpforms-tools-entries-export-cancel' ), $processMsg : $( '#wpforms-tools-entries-export-process-msg' ), $optionFields : $( '#wpforms-tools-entries-export-options-type-info' ), $selectStatuses : $( '#wpforms-tools-entries-export-select-statuses' ), $optionStatuses : $( '#wpforms-tools-entries-export-options-status' ), $clearDateButton : $( '.wpforms-clear-datetime-field' ), }; /** * Shorthand to translated strings. * * @since 1.5.5 * * @type {Object} */ const i18n = wpforms_tools_entries_export.i18n; /** * Runtime variables. * * @since 1.5.5 * * @type {Object} */ const vars = {}; /** * Public functions and properties. * * @since 1.5.5 * * @type {Object} */ const app = { /** * Forms data cached. * * @since 1.5.5 * * @type {Object} */ formsCache: {}, /** * Start the engine. * * @since 1.5.5 */ init() { $( app.ready ); }, /** * Document ready. * * @since 1.5.5 */ ready() { vars.processing = false; app.initChoices(); app.initDateRange(); app.initFormContainer(); app.initSubmit(); app.events(); }, /** * Register JS events. * * @since 1.5.5 */ events() { // Selecting form. el.$selectForm[ 0 ].addEventListener( 'choice', function( event ) { app.selectFormEvent( event ); } ); // Toggle all checkboxes on or off. $( document ).on( 'change', '#wpforms-tools-entries-export-options .wpforms-toggle-all', function() { const $this = $( this ), $toggle = $this.find( 'input' ), $options = $this.siblings().find( 'input' ); $options.prop( 'checked', $toggle.prop( 'checked' ) ); } ); // Update toggle all state when changing individual checkbox. $( document ).on( 'change', '#wpforms-tools-entries-export-options-fields-checkboxes label, #wpforms-tools-entries-export-options-payment-fields-checkboxes label, #wpforms-tools-entries-export-options-additional-info label', function() { const $this = $( this ); if ( $this.hasClass( 'wpforms-toggle-all' ) ) { return; } const $options = $this.parent().find( 'label' ).not( '.wpforms-toggle-all' ).find( 'input' ); const $checked = $options.filter( ':checked' ); const $toggle = $this.siblings( '.wpforms-toggle-all' ).find( 'input' ); $toggle.prop( 'checked', $checked.length === $options.length ); } ); // Display file download error. $( document ).on( 'csv_file_error', function( e, msg ) { app.displaySubmitMessage( msg, 'error' ); } ); // Display dynamic columns notice. $( document ).on( 'change', '#wpforms-tools-entries-export-options-type-info input', function() { app.switchDynamicColumnsNotice( $( this ) ); } ); // Clear date field. $( document ).on( 'click', '.wpforms-clear-datetime-field', function( e ) { e.preventDefault(); el.$dateFlatpickr.flatpickr().clear(); el.$dateFlatpickr[ 0 ].removeAttribute( 'value' ); $( this ).addClass( 'wpforms-hidden' ); // Reinitialization date range input to correct work after clear. app.initDateRange(); } ); }, /** * Select form event. * * @since 1.8.5 * * @param {Object} event Event. */ selectFormEvent( event ) { if ( event.detail.choice.placeholder ) { el.$expOptions.addClass( 'hidden' ); return; } if ( vars.formID === event.detail.choice.value ) { return; } vars.formID = event.detail.choice.value; app.resetChoices(); el.$optionStatuses.removeClass( 'wpforms-hidden' ); if ( 'undefined' === typeof app.formsCache[ vars.formID ] ) { app.retrieveFormAndRenderFields(); } else { if ( app.formsCache[ vars.formID ].statuses.length > 1 ) { app.setChoices( app.formsCache[ vars.formID ].statuses ); } else { el.$optionStatuses.addClass( 'wpforms-hidden' ); } // Render cached form fields checkboxes. app.renderFields( app.formsCache[ vars.formID ].fields ); // Render cached payment fields checkboxes. app.renderFields( app.formsCache[ vars.formID ].paymentFields, true ); app.handleSearchFields( app.formsCache[ vars.formID ].fields, app.formsCache[ vars.formID ].paymentFields ); app.optionsFields( app.formsCache[ vars.formID ].dynamicColumns ); app.addDynamicColumnsNotice( app.formsCache[ vars.formID ].dynamicColumnsNotice ); } }, /** * Switch dynamic columns notice. * * @since 1.8.5 * * @param {Object} input Input. */ switchDynamicColumnsNotice( input ) { const inputValue = input.val(); if ( inputValue === 'dynamic_columns' ) { const $notice = input.parent().find( '.wpforms-tools-entries-export-notice-warning' ); $notice.toggleClass( 'wpforms-hide' ); } }, /** * Retrieve the form fields and render fields checkboxes. * * @since 1.5.5 */ retrieveFormAndRenderFields() { vars.ajaxData = { action: 'wpforms_tools_entries_export_form_data', nonce: wpforms_tools_entries_export.nonce, form: vars.formID, }; el.$selectFormSpinner.removeClass( 'hidden' ); app.displayFormsMessage( '' ); $.get( ajaxurl, vars.ajaxData ) .done( function( res ) { if ( res.success ) { // Render form fields checkboxes. app.renderFields( res.data.fields ); // Render payment fields checkboxes. app.renderFields( res.data.payment_fields, true ); app.optionsFields( res.data.dynamic_columns ); if ( res.data.dynamic_columns ) { app.addDynamicColumnsNotice( res.data.dynamic_columns_notice ); } app.handleSearchFields( res.data.fields, res.data.payment_fields ); app.formsCache[ vars.formID ] = { fields: res.data.fields, paymentFields: res.data.payment_fields, dynamicColumns: res.data.dynamic_columns, dynamicColumnsNotice: res.data.dynamic_columns_notice, statuses: res.data.statuses, }; el.$expOptions.removeClass( 'hidden' ); if ( res.data.statuses.length > 1 ) { app.setChoices( res.data.statuses ); } else { el.$optionStatuses.addClass( 'wpforms-hidden' ); } } else { app.displayFormsMessage( res.data.error ); el.$expOptions.addClass( 'hidden' ); } } ) .fail( function( jqXHR, textStatus, errorThrown ) { app.displayFormsMessage( i18n.error_prefix + '
' + errorThrown ); el.$expOptions.addClass( 'hidden' ); } ) .always( function() { el.$selectFormSpinner.addClass( 'hidden' ); } ); }, /** * Export step ajax request. * * @since 1.5.5 * * @param {string} requestId Request Identifier. */ exportAjaxStep( requestId ) { if ( ! vars.processing ) { return; } const ajaxData = app.getAjaxPostData( requestId ); $.post( ajaxurl, ajaxData ) .done( function( res ) { let msg = ''; clearTimeout( vars.timerId ); if ( ! res.success ) { app.displaySubmitMessage( res.data.error, 'error' ); return; } if ( res.data.count === 0 ) { app.displaySubmitMessage( i18n.prc_2_no_entries ); return; } msg = i18n.prc_3_done; msg += '
' + i18n.prc_3_download + ', ' + i18n.prc_3_click_here + '.'; app.displaySubmitMessage( msg, 'info' ); app.triggerDownload( res.data.request_id ); } ) .fail( function( jqXHR, textStatus, errorThrown ) { clearTimeout( vars.timerId ); app.displaySubmitMessage( i18n.error_prefix + '
' + errorThrown, 'error' ); } ) .always( function() { app.displaySubmitSpinner( false ); } ); }, /** * Get export step ajax POST data. * * @since 1.5.5 * * @param {string} requestId Request Identifier. * * @return {Object} Ajax POST data. */ getAjaxPostData( requestId ) { let ajaxData; if ( requestId === 'first-step' ) { const statuses = []; ajaxData = el.$form.serializeArray().reduce( function( obj, item ) { if ( item.name === 'statuses' ) { statuses.push( item.value ); } else { obj[ item.name ] = item.value; } return obj; }, {} ); if ( el.$fieldsCheckboxes.find( 'input' ).length < 1 ) { ajaxData.date = ''; ajaxData[ 'search[term]' ] = ''; } ajaxData.statuses = statuses; } else { ajaxData = { action: 'wpforms_tools_entries_export_step', nonce: wpforms_tools_entries_export.nonce, // eslint-disable-next-line camelcase request_id: requestId, }; } return ajaxData; }, /** * Submit button click. * * @since 1.5.5 */ initSubmit() { el.$submitButton.on( 'click', function( e ) { e.preventDefault(); const $t = $( this ); if ( $t.hasClass( 'wpforms-btn-spinner-on' ) ) { return; } el.$submitButton.blur(); app.displaySubmitSpinner( true ); app.displaySubmitMessage( '' ); vars.timerId = setTimeout( function() { app.displaySubmitMessage( i18n.prc_1_filtering + '
' + i18n.prc_1_please_wait, 'info' ); }, 3000 ); app.exportAjaxStep( 'first-step' ); } ); el.$cancelButton.on( 'click', function( e ) { e.preventDefault(); el.$cancelButton.blur(); app.displaySubmitMessage( '' ); app.displaySubmitSpinner( false ); } ); }, /** * Init Form container. * * @since 1.5.5 */ initFormContainer() { if ( wpforms_tools_entries_export.form_id > 0 ) { el.$expOptions.removeClass( 'hidden' ); if ( el.$fieldsCheckboxes.find( 'input' ).length < 1 ) { el.$dateSection.addClass( 'hidden' ); el.$searchSection.addClass( 'hidden' ); } } }, /** * Init Choices. * * @since 1.8.5 */ initChoices() { vars.Choices = new Choices( el.$selectStatuses[ 0 ], { removeItemButton: true, allowHTML: false, // TODO: Remove after next Choices.js release. itemSelectText: '', } ); }, /** * Reset Choices. * * @since 1.8.5 */ resetChoices() { vars.Choices.clearInput(); vars.Choices.clearStore(); vars.Choices.setChoices( [], 'value', 'label', true ); }, /** * Set Choices. * Exclude choice with value 'spam'. * * @since 1.8.5 * * @param {Array} choices Choices. */ setChoices( choices ) { // Try to get 'spam' choice. const spamChoice = choices.filter( function( choice ) { return choice.value === 'spam'; } )[ 0 ]; // Set 'spam' choice. if ( spamChoice ) { vars.Choices.setChoices( [ spamChoice ], 'value', 'label', true ); } // Exclude choice with value 'spam'. choices = choices.filter( function( choice ) { return choice.value !== 'spam'; } ); // Select all choices. vars.Choices.setValue( choices, 'value', 'label', true ); }, /** * Init Flatpickr at Date Range field. * * @since 1.5.5 */ initDateRange() { const langCode = wpforms_tools_entries_export.lang_code; const flatpickr = window.flatpickr; let flatpickrLocale = { rangeSeparator: ' - ', }; if ( flatpickr !== 'undefined' && flatpickr.hasOwnProperty( 'l10ns' ) && flatpickr.l10ns.hasOwnProperty( langCode ) ) { flatpickrLocale = flatpickr.l10ns[ langCode ]; flatpickrLocale.rangeSeparator = ' - '; } el.$dateFlatpickr.flatpickr( { altInput: true, altFormat: 'M j, Y', dateFormat: 'Y-m-d', locale: flatpickrLocale, mode: 'range', defaultDate: wpforms_tools_entries_export.dates, onChange( selectedDates ) { el.$clearDateButton.toggleClass( 'wpforms-hidden', selectedDates.length !== 2 ); }, } ); }, /** * Render fields checkboxes. * * @since 1.5.5 * * @param {Object} fields Form fields data. * @param {boolean} paymentFields Payment fields flag. */ renderFields( fields, paymentFields = false ) { if ( typeof fields !== 'object' ) { return; } const html = { checkboxes: '', options: '', }; const fieldsKeys = Object.keys( fields ); el.$paymentFieldsSection.show(); if ( fieldsKeys.length === 0 ) { html.checkboxes = '' + i18n.error_form_empty + ''; } else { html.checkboxes += ''; fieldsKeys.forEach( function( index ) { let ch = ''; const id = parseInt( fields[ index ].id, 10 ); ch = ch.replace( '{index}', parseInt( index, 10 ) + '-' + id ); ch = ch.replace( '{id}', id ); ch = ch.replace( '{label}', fields[ index ].label ); html.checkboxes += ch; let op = ''; op = op.replace( '{id}', id ); op = op.replace( '{label}', fields[ index ].label ); html.options += op; } ); el.$dateSection.removeClass( 'hidden' ); el.$searchSection.removeClass( 'hidden' ); } if ( paymentFields ) { el.$paymentFieldsCheckboxes.html( html.checkboxes ); } else { el.$fieldsCheckboxes.html( html.checkboxes ); } // Hide payment fields section if there are no payment fields. if ( paymentFields && fieldsKeys.length === 0 ) { el.$paymentFieldsSection.hide(); } const optiongroupType = paymentFields ? 'payment-fields' : 'form-fields'; const optiongroup = el.$searchField.find( 'optgroup[data-type="' + optiongroupType + '"]' ); // Remove all options except the first one after form change. optiongroup.find( 'option:not(:first-child)' ).remove(); if ( paymentFields ) { // Hide/show the first option with placeholder for payment fields. optiongroup.find( 'option:first-child' ).toggle( fieldsKeys.length === 0 ); } optiongroup.append( html.options ); }, /** * Hide date and search sections if there are no fields. * * @since 1.8.5.2 * * @param {Object} formFields Form fields. * @param {Object} paymentFields Payment fields. */ handleSearchFields( formFields, paymentFields ) { const formFieldsCount = Object.keys( formFields ).length; const paymentFieldsCount = Object.keys( paymentFields ).length; if ( formFieldsCount === 0 && paymentFieldsCount === 0 ) { el.$dateSection.addClass( 'hidden' ); el.$searchSection.addClass( 'hidden' ); } }, /** * Show/hide additional options. * * @since 1.8.5 * * @param {boolean} isDynamicColumns Is dynamic columns enabled. */ optionsFields( isDynamicColumns ) { app.switchDynamicColumns( isDynamicColumns ); if ( isDynamicColumns ) { // Reset the dynamic columns option after form change. el.$optionFields.find( 'input[value=dynamic_columns]' ).prop( 'checked', false ); } }, /** * Show/hide dynamic columns option. * * @since 1.8.5 * * @param {boolean} isDynamicColumns Is dynamic columns enabled. */ switchDynamicColumns( isDynamicColumns ) { const labelElement = el.$optionFields.find( 'input[value=dynamic_columns]' ).parent(); labelElement.toggle( isDynamicColumns ); }, /** * Add notice about dynamic columns. * * @since 1.8.5 * * @param {string} notice Notice. */ addDynamicColumnsNotice( notice ) { el.$optionFields.find( '.wpforms-tools-entries-export-notice-warning' ).remove(); const labelElement = el.$optionFields.find( 'input[value=dynamic_columns]' ).parent(); labelElement.append( '
' + notice + '
' ); }, /** * Show/hide submit button spinner. * * @since 1.5.5 * * @param {boolean} show Show or hide the submit button spinner. */ displaySubmitSpinner( show ) { if ( show ) { el.$submitButton.addClass( 'wpforms-btn-spinner-on' ); el.$cancelButton.removeClass( 'hidden' ); vars.processing = true; } else { el.$submitButton.removeClass( 'wpforms-btn-spinner-on' ); el.$cancelButton.addClass( 'hidden' ); vars.processing = false; } }, /** * Display error message under form selector. * * @since 1.5.5 * * @param {string} message Message. */ displayFormsMessage( message ) { el.$selectFormMsg.html( '

' + message + '

' ); if ( message.length > 0 ) { el.$selectFormMsg.removeClass( 'wpforms-hidden' ); } else { el.$selectFormMsg.addClass( 'wpforms-hidden' ); } }, /** * Display message under submit button. * * @since 1.5.5 * * @param {string} message Message. * @param {string} type Use 'error' for errors messages. */ displaySubmitMessage( message, type = '' ) { if ( type && type === 'error' ) { el.$processMsg.addClass( 'wpforms-error' ); } else { el.$processMsg.removeClass( 'wpforms-error' ); } el.$processMsg.html( '

' + message + '

' ); if ( message.length > 0 ) { el.$processMsg.removeClass( 'wpforms-hidden' ); } else { el.$processMsg.addClass( 'wpforms-hidden' ); } }, /** * Initiating file downloading. * * @since 1.5.5 * * @param {string} requestId Request ID. */ triggerDownload( requestId ) { let url = wpforms_tools_entries_export.export_page; url += '&action=wpforms_tools_entries_export_download'; url += '&nonce=' + wpforms_tools_entries_export.nonce; url += '&request_id=' + requestId; el.$expOptions.find( 'iframe' ).remove(); el.$expOptions.append( '' ); el.$processMsg.find( '.wpforms-download-link' ).attr( 'href', url ); }, }; // Provide access to public functions/properties. return app; }( document, window, jQuery ) ); // Initialize. WPFormsEntriesExport.init(); Home - Ping Services

RESIDENTIAL SERVICES

Elevate your resident experience with our branded app & managed services platform

Discover how Ping Services enhances residential living through innovative technology, tailored community experiences, and on-demand support.
Ping-Services-Homepage-App

Residents can find:

‣ Homeowners manuals
‣ Appliance manuals
‣ Useful forms
‣ Buildings insurance documents.

Residents can :

‣ Meet the team
‣ Make a request
‣ Book visitor guest suite

Residents can book various services :

‣ VIP concierge
‣ Chauffeur
‣ Dry cleaning
‣ Housekeeping
‣ Personal training
‣ Beauty services
‣ Removals
‣ Storage

Residents can :

‣ Find local coffee shops, bars and vegan eats
‣ Explore local entertainment
‣ Arrange pet care and grooming services
‣ Book car rental services

Residents can book:

‣ Live virtual Fitness Classes
‣ On Demand Fitness Classes
‣ Health and Wellbeing Workshops
‣ In Property Fitness Classes and Events

FEATURES

All-in-one app resident engagement platform

ping-services-branded-app

Branded Resident App

Our resident apps can be white-labeled to elevate and set your brand apart. We incorporate your colour palette, tone of voice, and imagery to create a distinct, personalised look and feel.

Management Hub

Save your property teams time with our easy-to-use Management Hub, which automates routine tasks, centralises data, and improves resident communication.

WHAT WE DO

Ping delivers community

My Community (Groups)

‣ My social
‣ My business
‣ My sports partners

My Wellness & My Lifestyle

‣ Groups and community
‣ Activities
‣ Wellbeing pop-up's
‣ Events and classes

My Neighbourhood

‣ Local offers
‣ Amenities
‣ Events

Resident Marketplace

‣ Items free or for sale
‣ List services and businesses

My-local-ping-services

My Local

‣ News & weather
‣ Live transport links
‣ Local events

WHAT WE DO

Ping delivers convenience

Ping delivers your services

Handle all your needs seamlessly, from check-ins, check-outs, requests, complaints and even those pesky lockout moments.

Ping delivers our services

From community events & channels, wellbeing services, hyperlocal offers, and state-of-the-art solutions for parcel delivery, to hot food, and refrigerated grocery deliveries.

Ping delivers other partners’ services

Includes gym memberships, parking, laundry, housekeeping, cleaning, self-storage, car club, retail offers, and discounts.

Ping delivers your services

Handle all your needs seamlessly, from check-ins, check-outs, requests, complaints and even those pesky lockout moments.

Ping delivers concierge team engagement

Push notifications, customer concierge requests and team updates.

WHAT WE DO

Ping delivers efficiency

Automate Incoming Deliveries

Parcels And Mail

Hot/Cold Food Deliveries

... And Much More!

Smart Pickup App

Smart Pickup
App

Contact Us

Smart Pickup Locker

Smart Pickup Locker

Contact Us

Smart Food Locker

Smart Food
Locker

Contact Us

Smart Pickup Cube

Smart Pickup Cube

Contact Us

Smart Pickup Room

Smart Pickup Room

Contact Us

WHY US?

A seamless digital experience

Visitor Management

Our Resident Visitor Management module allows residents to conveniently invite guests via the app.

Guests check in and out at the front desk, ensuring secure and streamlined visitor management. Enhance your guest experience with ease and efficiency

Amenity Booking

Our Amenity Spaces and Room Bookings module enables residents to reserve meeting rooms, co-working spaces, and lounges through the app.

Tablets at each entrance display real-time availability and allow instant booking, optimizing space use and enhancing the resident experience.

Digital Media Screens

Connect digital media screens across your property to communicate and engage our community. Publish updates in real time with one click.

Events & Experiences

Organise and promote resident events hassle free. Whether its social events, wellness initiatives or virtual experiences. Set up calendars, RSVP & much more.

PingServices-events-and-experiences

WHY US?

Enhanced partner services

For you

Transform your property into a digital village, bringing the charm of a traditional community to your building through one centralised app.

For your community

Provide residents with a convenient platform to access beloved services from locally approved providers and partners, all in one place.

WHY US?

A service-led approach

Detailed Account Manager

Personalised support with a single point of contact to ensure consistent and effective service delivery.

Quarterly Account Review Calls

Regular calls to discuss app performance, address issues, and strategise on enhancing resident engagement.

Comprehensive Training

Offering training sessions and instructional videos to help property management teams and residents utilise all app features.

Ongoing Content Updates

Support with content changes and updates to keep the app current and relevant to resident needs.

WHY US?

Convenience With Every Click

Real-Time Support

Providing on-going support to resolve technical issues and answer queries promptly.

Resident Engagement Initiatives

Collaborating with clients to develop and implement strategies to boost resident interaction and satisfaction.

Feedback Mechanism

Establishing a structured feedback loop for continuous app improvement based on user input and suggestions.

Usage Analytics

Delivering detailed reports on app usage, offering insights to optimize functionality and engagement.

OUR IMPACT

Benefits for your tenants & team

CONTACT

Contact us

If you’re interested in deploying your own residents app, we’re here to help. Please contact us today for more information.

Set-up a discovery call today

Fill out the form and a member of our team will contact you back as soon as we are able.