From 535bd23b2d1f1df538347e575fa146b64c74afe4 Mon Sep 17 00:00:00 2001 From: Abdalla Dimes Date: Wed, 25 Oct 2023 18:51:34 +0300 Subject: [PATCH 1/2] feat: Added AI state seeder to the plugin --- control/content/app.services.js | 214 ++++++++++++++++++ .../controllers/content.home.controller.js | 68 +----- control/content/index.html | 1 + .../controllers/design.home.controller.js | 38 +--- widget/app.services.js | 21 +- widget/controllers/widget.home.controller.js | 39 +--- 6 files changed, 252 insertions(+), 129 deletions(-) diff --git a/control/content/app.services.js b/control/content/app.services.js index f65096b..cde4d9b 100644 --- a/control/content/app.services.js +++ b/control/content/app.services.js @@ -158,4 +158,218 @@ } } }]) + .factory('DefaultInfo', ['LAYOUTS', function(LAYOUTS) { + return { + content: { + carouselImages: [], + description: '

 

', + addressTitle: '', + address: { + type:'', + location:'', + location_coordinates: [], + }, + links: [], + showMap: false + }, + design: { + listLayout: LAYOUTS.listLayouts[0].name, + backgroundImage: '' + } + } + }]) + .factory('AIStateSeeder', ['DefaultInfo', 'DataStore', 'TAG_NAMES', function(DefaultInfo, DataStore, TAG_NAMES, ) { + let stateSeederInstance; + let ContactInfo = DefaultInfo; + const jsonTemplate = { + imagesURLs: [], + description: '', + phoneNumber: '', + email: '', + location: '', + } + + const getAddress = function(location) { + return new Promise((resolve) => { + if (location) { + const geocoder = new google.maps.Geocoder(); + geocoder.geocode({'address': location}, function(result, status) { + if (status == google.maps.GeocoderStatus.OK) { + resolve( + { + type: 'Location', + location: location, + location_coordinates: [result[0].geometry.location.lng(), result[0].geometry.location.lat()] + }); + } else { + resolve({ + type: 'Location', + location: location, + location_coordinates: [] + }); + } + }) + } else { + resolve(null); + } + }) + } + + const parseImageURL = function(url) { + const optimizedURL = url.replace('1080x720', '100x100'); + return new Promise((resolve) => { + if (url.includes("http")){ + const xhr = new XMLHttpRequest(); + xhr.open("GET", optimizedURL); + xhr.onerror = (error) => { + console.warn('provided URL is not a valid image', error); + resolve(''); + } + xhr.onload = () => { + if (xhr.responseURL.includes('source-404') || xhr.status == 404) { + return resolve(''); + } else { + return resolve(xhr.responseURL.replace('h=100', 'h=720').replace('w=100', 'w=1080')); + } + }; + xhr.send(); + } else resolve(''); + }); + } + + const getCurrentUser = function () { + return new Promise((resolve, reject) => { + buildfire.auth.getCurrentUser((err, currentUser) => { + if (err) reject(err); + resolve(currentUser); + }); + }); + } + + const _applyDefaults = function (data) { + // create HTML div element for the description, to avoid breaking the WYSIWYG + const descriptionElement = document.createElement('div'); + descriptionElement.innerHTML = data.description || ''; + // default address in case there was no address provided + let address = { + type: 'Location', + location: '501 Pacific Hwy, San Diego, CA 92101, USA', + location_coordinates: [-117.17096400000003,32.7100444] + } + if (data.address) { + address = data.address; + } + if (data.imagesURLs && data.imagesURLs.length) { + for (let i = 0; i < data.imagesURLs.length; i++) { + data.imagesURLs[i] = { + action: 'noAction', + iconUrl: data.imagesURLs[i], + title: 'image' + } + } + } else { + data.imagesURLs = []; + } + + // add links based on the contact info provided + let links = []; + const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/; + if (data.email && data.email.match(emailRegex)) { + links.push({ + title: 'Email', + action:'sendEmail', + email: data.email + }); + } + if (data.phoneNumber) { + links.push({ + title:'Call', + action:'callNumber', + phoneNumber: data.phoneNumber + }); + } + return { + showMap: true, + carouselImages: data.imagesURLs, + description : descriptionElement.outerHTML, + addressTitle: 'Navigate to Location', + address: address, + links: links, + } + } + + const handleAIReq = function(callback ,err, response) { + if ( + err || + !response || + typeof response !== 'object' || + !Object.keys(response).length || !response.data + ) { + return buildfire.dialog.toast({ + message: 'Bad AI request, please try changing your request.', + type: 'danger', + }); + } + + let optimizedURLs = []; + let promises = response.data.imagesURLs.map(url => { + return parseImageURL(url) + }); + + Promise.allSettled(promises).then(parsingResults => { + parsingResults.forEach(parsingResult => { + if (parsingResult.status == 'fulfilled' && parsingResult.value) { + optimizedURLs.push(parsingResult.value); + } + }) + response.data.imagesURLs = optimizedURLs; + DataStore.get(TAG_NAMES.CONTACT_INFO).then(info => { + if (info && info.data && Object.keys(info.data).length) { + ContactInfo = info.data; + } + getAddress(response.data.location).then(locationResult => { + response.data.address = locationResult; + ContactInfo.content = _applyDefaults(response.data); + DataStore.save(ContactInfo, TAG_NAMES.CONTACT_INFO).then(() => { + stateSeederInstance?.requestResult?.complete(); + callback(); + }).catch(err => { + stateSeederInstance?.requestResult?.complete(); + console.warn('error saving data to datastore', err); + return buildfire.dialog.toast({ + message: 'Something went wrong, try again later.', + type: 'danger', + }); + }); + }); + }).catch(err => { + stateSeederInstance?.requestResult?.complete(); + console.warn('error getting data from datastore', err); + return buildfire.dialog.toast({ + message: 'Something went wrong, try again later.', + type: 'danger', + }); + }); + }); + }; + + return { + initStateSeeder: function(callback) { + getCurrentUser().then(user => { + stateSeederInstance = new buildfire.components.aiStateSeeder({ + generateOptions: { + userMessage: `Generate a contact us information related to [business-type] located in [San Diego, CA, USA].\nFor phone number use [+1 555 555-1234].\nFor email use [${user?.email || ''}].`, + maxRecords: 5, + systemMessage: + 'images are two 1080x720 images URLs related to location, use source.unsplash.com for images, URL should not have premium_photo or source.unsplash.com/random. return description as HTML', + jsonTemplate: jsonTemplate, + callback: handleAIReq.bind(this, callback), + hintText: 'Replace values between brackets to match your requirements.', + }, + }).smartShowEmptyState(); + }); + return true; + }, + } + }]) })(window.angular, window.buildfire); \ No newline at end of file diff --git a/control/content/controllers/content.home.controller.js b/control/content/controllers/content.home.controller.js index a3efb10..9c968d2 100644 --- a/control/content/controllers/content.home.controller.js +++ b/control/content/controllers/content.home.controller.js @@ -3,61 +3,12 @@ (function (angular) { angular .module('contactUsPluginContent') - .controller('ContentHomeCtrl', ['$scope', 'Buildfire', 'LAYOUTS', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ADDRESS_TYPE', 'Utils', '$timeout', - function ($scope, Buildfire, LAYOUTS, DataStore, TAG_NAMES, STATUS_CODE, ADDRESS_TYPE, Utils, $timeout) { - var _data = { - "content": { - "carouselImages": [], - "description": '

 

', - "addressTitle": "", - "address": { - type:"", - location:"", - location_coordinates:[] - }, - "links": [], - "showMap": false - }, - "design": { - "listLayout": LAYOUTS.listLayouts[0].name, - "backgroundImage": "" - } - }; - - // var initDummyFlag=true; - - //init dummy data - var _dummyData= { - content: { - showMap:true, - carouselImages: [{ - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6ac49110-11c7-11e6-92ea-27ed66023d52.jpeg?fit=crop&w=342&h=193", - title: "image" - }, - { - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6bf3c240-11c7-11e6-ad08-375cc71b6ca7.jpg?fit=crop&w=342&h=193", - title: "image" - }], - description : "

With the wysiwyg, you can include text and lists, embed images, embed videos, and link to webpages, emails, phone numbers and more. Check out the tutorial on the wysiwyg for detailed information.

", - addressTitle:"", - address:{ - type:"Location", - location:"501 Pacific Hwy, San Diego, CA 92101, USA", - location_coordinates:[-117.17096400000003,32.7100444] - }, - links:[{"title":"Call","action":"callNumber","phoneNumber":"6195551234"},{"title":"Email","action":"sendEmail"}] - }, - design:{ - listLayout:"Layout_1", - backgroundImage:"" - }, - default : true - }; + .controller('ContentHomeCtrl', ['$scope', 'Buildfire', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ADDRESS_TYPE', 'Utils', '$timeout', 'DefaultInfo', 'AIStateSeeder', + function ($scope, Buildfire, DataStore, TAG_NAMES, STATUS_CODE, ADDRESS_TYPE, Utils, $timeout, DefaultInfo, AIStateSeeder) { + AIStateSeeder.initStateSeeder(() => {init()}); + var _data = DefaultInfo; var ContentHome = this; - ContentHome.masterData = _dummyData; -// ContentHome.data = angular.copy(_data); + ContentHome.data = angular.copy(_data); ContentHome.validCoordinatesFailure = false; // create a new instance of the buildfire carousel editor @@ -103,7 +54,7 @@ $scope.$digest(); }; - updateMasterItem(_dummyData); + updateMasterItem(_data); ContentHome.bodyWYSIWYGOptions = { plugins: 'advlist autolink link image lists charmap print preview', @@ -126,7 +77,6 @@ var init = function () { var success = function (result) { if (result && result.id && result.data) { - // initDummyFlag=false; console.info('init success result:', result); ContentHome.data = result.data; if(!ContentHome.data) { @@ -155,9 +105,9 @@ updateMasterItem(ContentHome.data); if (tmrDelay)clearTimeout(tmrDelay); }else{ - //initDummyFlag=true; - ContentHome.data=_dummyData; - // $scope.$digest(); + + ContentHome.data= _data; + if (ContentHome.data.content) { if (!ContentHome.data.content.carouselImages) editor.loadItems([]); diff --git a/control/content/index.html b/control/content/index.html index df2d8f8..cba4fbf 100644 --- a/control/content/index.html +++ b/control/content/index.html @@ -16,6 +16,7 @@ + diff --git a/control/design/controllers/design.home.controller.js b/control/design/controllers/design.home.controller.js index a36b30c..1948c62 100644 --- a/control/design/controllers/design.home.controller.js +++ b/control/design/controllers/design.home.controller.js @@ -3,8 +3,8 @@ (function (angular, window) { angular .module('contactUsPluginDesign') - .controller('DesignHomeCtrl', ['$scope','Buildfire','LAYOUTS','DataStore','TAG_NAMES', - function ($scope, Buildfire, LAYOUTS,DataStore,TAG_NAMES) { + .controller('DesignHomeCtrl', ['$scope','Buildfire','LAYOUTS','DataStore','TAG_NAMES', 'DefaultInfo', + function ($scope, Buildfire, LAYOUTS, DataStore, TAG_NAMES, DefaultInfo) { var DesignHome = this; var DesignHomeMaster; DesignHome.layouts = { @@ -14,37 +14,7 @@ ] }; - //init dummy data - var _dummyData= { - content: { - showMap:true, - carouselImages: [{ - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6ac49110-11c7-11e6-92ea-27ed66023d52.jpeg?fit=crop&w=342&h=193", - title: "image" - }, - { - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6bf3c240-11c7-11e6-ad08-375cc71b6ca7.jpg?fit=crop&w=342&h=193", - title: "image" - }], - description : "

With the wysiwyg, you can include text and lists, embed images, embed videos, and link to webpages, emails, phone numbers and more. Check out the tutorial on the wysiwyg for detailed information.

", - addressTitle:"", - address:{ - type:"Location", - location:"501 Pacific Hwy, San Diego, CA 92101, USA", - location_coordinates:[-117.17096400000003,32.7100444] - }, - links:[{"title":"Call","action":"callNumber","phoneNumber":"6195551234"},{"title":"Email","action":"sendEmail"}] - }, - design:{ - listLayout:"Layout_1", - backgroundImage:"" - } - }; - - DesignHomeMaster=_dummyData; - + DesignHomeMaster = DefaultInfo; /*On layout click event*/ DesignHome.changeItemLayout = function (layoutName) { @@ -89,7 +59,7 @@ }; function init() { - var _data = _dummyData; + var _data = DefaultInfo; /* background image add */ Buildfire.datastore.get(TAG_NAMES.CONTACT_INFO, function (err, data) { diff --git a/widget/app.services.js b/widget/app.services.js index 369551b..3f5507f 100644 --- a/widget/app.services.js +++ b/widget/app.services.js @@ -135,5 +135,24 @@ } }; }]) - ; + .factory('DefaultInfo', ['LAYOUTS', function(LAYOUTS) { + return { + content: { + carouselImages: [], + description: '

 

', + addressTitle: '', + address: { + type:'', + location:'', + location_coordinates:[] + }, + links: [], + showMap: false + }, + design: { + listLayout: LAYOUTS.listLayouts[0].name, + backgroundImage: '' + } + } + }]) })(window.angular, window.buildfire); \ No newline at end of file diff --git a/widget/controllers/widget.home.controller.js b/widget/controllers/widget.home.controller.js index bf97114..d199fe8 100644 --- a/widget/controllers/widget.home.controller.js +++ b/widget/controllers/widget.home.controller.js @@ -2,8 +2,8 @@ (function (angular, buildfire) { angular.module('contactUsPluginWidget') - .controller('WidgetHomeCtrl', ['$routeParams', 'Buildfire', 'DataStore', '$scope', 'TAG_NAMES', 'Location', 'LAYOUTS', '$rootScope', '$sce', '$timeout', - function ($routeParams, Buildfire, DataStore, $scope, TAG_NAMES, Location, LAYOUTS, $rootScope, $sce, $timeout) { + .controller('WidgetHomeCtrl', ['Buildfire', 'DataStore', '$scope', 'TAG_NAMES', 'LAYOUTS', '$rootScope', '$sce', '$timeout', 'DefaultInfo', + function (Buildfire, DataStore, $scope, TAG_NAMES, LAYOUTS, $rootScope, $sce, $timeout, DefaultInfo) { var WidgetHome = this; var currentListLayout = null; WidgetHome.data = {}; @@ -29,38 +29,7 @@ }); }); - var _dummyData = { - content: { - showMap: true, - carouselImages: [{ - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6ac49110-11c7-11e6-92ea-27ed66023d52.jpeg?fit=crop&w=342&h=193", - title: "image" - }, - { - action: "noAction", - iconUrl: "http://buildfire.imgix.net/1462345835888-04866688400506973/6bf3c240-11c7-11e6-ad08-375cc71b6ca7.jpg?fit=crop&w=342&h=193", - title: "image" - }], - description: "

With the wysiwyg, you can include text and lists, embed images, embed videos, and link to webpages, emails, phone numbers and more. Check out the tutorial on the wysiwyg for detailed information.

", - addressTitle: "", - address: { - type: "Location", - location: "501 Pacific Hwy, San Diego, CA 92101, USA", - location_coordinates: [-117.17096400000003, 32.7100444] - }, - links: [{ - "title": "Call", - "action": "callNumber", - "phoneNumber": "6195551234" - }, { "title": "Email", "action": "sendEmail" }] - }, - design: { - listLayout: "Layout_1", - backgroundImage: "" - } - }; - + var _data = DefaultInfo; /*declare the device width heights*/ $rootScope.deviceHeight = window.innerHeight; $rootScope.deviceWidth = window.innerWidth || 320; @@ -124,7 +93,7 @@ var success = function (result) { if (!result.id) { console.log('NO DATA AVAILABLE'); - WidgetHome.data = _dummyData; + WidgetHome.data = _data; } else { WidgetHome.data = result.data; } From 468e12c8d0e8835990a4b820b4152cad89526cec Mon Sep 17 00:00:00 2001 From: Abdalla Dimes Date: Tue, 31 Oct 2023 18:39:23 +0300 Subject: [PATCH 2/2] fix: Implemented code review changes and fixed bugs --- control/content/app.js | 16 +++++++++++ control/content/app.services.js | 27 ++++++++++--------- .../controllers/content.home.controller.js | 7 ++--- control/design/app.services.js | 20 ++++++++++++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/control/content/app.js b/control/content/app.js index 4cba7da..8386755 100644 --- a/control/content/app.js +++ b/control/content/app.js @@ -48,6 +48,22 @@ }); } }); + // fix google places autocomplete dropdown position + setTimeout(() => { + const target = document.querySelector('.pac-container'); + if (target) { + const observer = new MutationObserver(() => { + document.querySelectorAll('.pac-item span, .pac-item') + .forEach((n) => n.classList.add('needsclick')); + const autocompleteBoundaries = document.getElementById('googleMapAutocomplete').getBoundingClientRect(); + target.style.top = (autocompleteBoundaries.top + autocompleteBoundaries.height )+ 'px'; + }); + observer.observe(target, { childList: true, attributes: true, + characterData: true, + }); + } + console.log('observer target :', target); + }, 2000); } }; }) diff --git a/control/content/app.services.js b/control/content/app.services.js index cde4d9b..3163dbd 100644 --- a/control/content/app.services.js +++ b/control/content/app.services.js @@ -178,7 +178,7 @@ } } }]) - .factory('AIStateSeeder', ['DefaultInfo', 'DataStore', 'TAG_NAMES', function(DefaultInfo, DataStore, TAG_NAMES, ) { + .factory('AIStateSeeder', ['DefaultInfo', 'DataStore', 'TAG_NAMES', '$rootScope', function(DefaultInfo, DataStore, TAG_NAMES, $rootScope ) { let stateSeederInstance; let ContactInfo = DefaultInfo; const jsonTemplate = { @@ -198,7 +198,7 @@ resolve( { type: 'Location', - location: location, + location: result[0].formatted_address || location, location_coordinates: [result[0].geometry.location.lng(), result[0].geometry.location.lat()] }); } else { @@ -207,13 +207,13 @@ location: location, location_coordinates: [] }); - } - }) + }; + }); } else { resolve(null); - } - }) - } + }; + }); + }; const parseImageURL = function(url) { const optimizedURL = url.replace('1080x720', '100x100'); @@ -278,7 +278,8 @@ links.push({ title: 'Email', action:'sendEmail', - email: data.email + email: data.email, + subject: 'Contact US', }); } if (data.phoneNumber) { @@ -298,7 +299,7 @@ } } - const handleAIReq = function(callback ,err, response) { + const handleAIReq = function(err, response) { if ( err || !response || @@ -332,7 +333,7 @@ ContactInfo.content = _applyDefaults(response.data); DataStore.save(ContactInfo, TAG_NAMES.CONTACT_INFO).then(() => { stateSeederInstance?.requestResult?.complete(); - callback(); + $rootScope.initContentHome(); }).catch(err => { stateSeederInstance?.requestResult?.complete(); console.warn('error saving data to datastore', err); @@ -354,16 +355,16 @@ }; return { - initStateSeeder: function(callback) { + initStateSeeder: function() { getCurrentUser().then(user => { stateSeederInstance = new buildfire.components.aiStateSeeder({ generateOptions: { - userMessage: `Generate a contact us information related to [business-type] located in [San Diego, CA, USA].\nFor phone number use [+1 555 555-1234].\nFor email use [${user?.email || ''}].`, + userMessage: `Generate a contact us information related to [business-type] located in [target-region].\nFor phone number use [+1 555 555-1234].\nFor email use [${user?.email || ''}].`, maxRecords: 5, systemMessage: 'images are two 1080x720 images URLs related to location, use source.unsplash.com for images, URL should not have premium_photo or source.unsplash.com/random. return description as HTML', jsonTemplate: jsonTemplate, - callback: handleAIReq.bind(this, callback), + callback: handleAIReq.bind(this), hintText: 'Replace values between brackets to match your requirements.', }, }).smartShowEmptyState(); diff --git a/control/content/controllers/content.home.controller.js b/control/content/controllers/content.home.controller.js index 9c968d2..f0d2683 100644 --- a/control/content/controllers/content.home.controller.js +++ b/control/content/controllers/content.home.controller.js @@ -3,9 +3,10 @@ (function (angular) { angular .module('contactUsPluginContent') - .controller('ContentHomeCtrl', ['$scope', 'Buildfire', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ADDRESS_TYPE', 'Utils', '$timeout', 'DefaultInfo', 'AIStateSeeder', - function ($scope, Buildfire, DataStore, TAG_NAMES, STATUS_CODE, ADDRESS_TYPE, Utils, $timeout, DefaultInfo, AIStateSeeder) { - AIStateSeeder.initStateSeeder(() => {init()}); + .controller('ContentHomeCtrl', ['$scope', 'Buildfire', 'DataStore', 'TAG_NAMES', 'STATUS_CODE', 'ADDRESS_TYPE', 'Utils', '$timeout', 'DefaultInfo', 'AIStateSeeder', '$rootScope', + function ($scope, Buildfire, DataStore, TAG_NAMES, STATUS_CODE, ADDRESS_TYPE, Utils, $timeout, DefaultInfo, AIStateSeeder, $rootScope) { + AIStateSeeder.initStateSeeder(); + $rootScope.initContentHome = () => {init()}; var _data = DefaultInfo; var ContentHome = this; ContentHome.data = angular.copy(_data); diff --git a/control/design/app.services.js b/control/design/app.services.js index 42611c6..9dbd080 100644 --- a/control/design/app.services.js +++ b/control/design/app.services.js @@ -41,4 +41,24 @@ } } }]) + .factory('DefaultInfo', ['LAYOUTS', function(LAYOUTS) { + return { + content: { + carouselImages: [], + description: '

 

', + addressTitle: '', + address: { + type:'', + location:'', + location_coordinates: [], + }, + links: [], + showMap: false + }, + design: { + listLayout: LAYOUTS.listLayouts[0].name, + backgroundImage: '' + } + } + }]) })(window.angular, window.buildfire); \ No newline at end of file