diff --git a/control/content/app.services.js b/control/content/app.services.js index a54a1ac..1afe593 100644 --- a/control/content/app.services.js +++ b/control/content/app.services.js @@ -247,5 +247,209 @@ buildfire.analytics.unregisterEvent('coupon_item_view_' + key); } }; - }]);; + }]).factory('StateSeeder', ['TAG_NAMES', 'DataStore', 'RankOfLastItem', '$rootScope', '$timeout' ,function(TAG_NAMES, DataStore, RankOfLastItem, $rootScope, $timeout) { + let itemsList; + let stateSeederInstance; + $rootScope.oldCouponsIds = []; + let jsonTemplate = { + items: [ + { + title: "", + summary: "", + listImage: "", + }, + ], + }; + let handleAIReq = function(isImport, err, data) { + if ( + err || + !data || + typeof data !== "object" || + !Object.keys(data).length || !data.data || !data.data.items || !data.data.items.length + ) { + return buildfire.dialog.toast({ + message: "Bad AI request, please try changing your request.", + type: "danger", + }); + } + itemsList = data.data.items; + //Check image URLs + let coupons = itemsList.map(item => { + return new Promise((resolve, reject) => { + checkNotFoundImages(item.listImage ?? "", isImport).then(res => { + if (res.isValid) { + item.listImage = res.newURL; + resolve(item); + } else { + reject('image URL not valid'); + } + }) + }) + }) + + // Check image URLs + Promise.allSettled(coupons).then(results => { + itemsList = []; + results.forEach(res => { + if(res.status == 'fulfilled') { + const coupon = res.value; + if (coupon) { + itemsList.push(coupon); + } + } + }) + if (!itemsList.length) { + stateSeederInstance?.requestResult?.complete(); + return buildfire.dialog.toast({ + message: "Bad AI request, please try changing your request.", + type: "danger", + }); + } + + // reset old data + checkOldData().then(() => { + // save new data + buildfire.messaging.sendMessageToWidget({ type: "ImportCSV", importing: true }); + let promises = itemsList.map((item, i) => { + return new Promise((resolve, reject) => { + itemsList[i] = _applyDefaults(itemsList[i]); + RankOfLastItem.setRank(itemsList[i].rank); + DataStore.insert(itemsList[i], TAG_NAMES.COUPON_ITEMS).then((res)=> { + if (res) { + $rootScope.oldCouponsIds.push(res.id); + itemsList[i].deepLinkId = res.id, + itemsList[i].deepLinkUrl = buildfire.deeplink.createLink({ id: res.id }) + new Deeplink({ + deeplinkId: res.id, + name: res.data.title, + imageUrl: res.data.listImage ? res.data.listImage : null, + deeplinkData: { + id: res.id, + } + }).save((err, deepLinkData) => { + itemsList[i].deepLinkId = deepLinkData.deeplinkId; + DataStore.update(res.id, itemsList[i], TAG_NAMES.COUPON_ITEMS).then(() => { + }) + resolve(); + }); + } + }) + + }); + }) + Promise.allSettled(promises).then(() => { + $timeout(()=> { + buildfire.messaging.sendMessageToWidget({ type: "ImportCSV", importing: false }); + $rootScope.reloadCoupons = true; + }) + $rootScope.reloadCoupons = true; + }).catch(err => console.warn('error while saving data: ', err)) + + }) + stateSeederInstance?.requestResult?.complete(); + }) + } + + // UTILITIES + let _applyDefaults = function(item) { + if (item.title) { + return { + title: item.title, + summary: item.summary || "", + listImage: item.listImage || "", + startOn: Date.now(), + expiresOn: Math.trunc(Date.now() + Math.random() * 8640000000), + links: [], + preRedemptionText: "Redeem Now", + postRedemptionText: "Coupon Redeemed", + carouselImages: item.listImage ? [ + { + "action": "noAction", + "iconUrl": item.listImage, + "title": "image" + } + ] : [], + rank: RankOfLastItem.getRank() ? RankOfLastItem.getRank() + 10: 10, + addressTitle: "", + location: { + addressTitle: "", + coordinates: { + lat: "", + lng: "" + } + }, + Categories: [], + reuseAfterInMinutes: -1, + dateCreated: Date.now(), + deepLinkUrl: '', // must have an id from datatore + deepLinkId: '', //same as item id + SelectedCategories: [], + } + } + return null + } + + let checkNotFoundImages = function(url, isImport) { + const optimisedURL = url.replace('1080x720', '100x100'); + return new Promise((resolve) => { + if (url.includes("http")){ + const xhr = new XMLHttpRequest(); + xhr.open("GET", optimisedURL); + xhr.onerror = (error) => { + console.warn('provided URL is not a valid image', error); + resolve({isValid: true, newURL: isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png'}); + } + xhr.onload = () => { + if (xhr.responseURL.includes('source-404') || xhr.status == 404) { + return resolve({isValid: true ,newURL: isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png'}); + } else { + return resolve({isValid: true, newURL: xhr.responseURL.replace('h=100', 'h=720').replace('w=100', 'w=1080') }); + } + }; + xhr.send(); + } else resolve({isValid: true ,newURL: isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png'}); + }); + };`` + + let checkOldData = function() { + return new Promise(resolve => { + if (stateSeederInstance.requestResult.resetData){ + $rootScope.oldCouponsIds.forEach(id => { + Deeplink.deleteById(id); + }) + $rootScope.oldCouponsIds = []; + DataStore.save([], TAG_NAMES.COUPON_ITEMS).then(() => { + resolve(); + }) + } else { + resolve(); + } + }) + } + + return { + initStateSeeder: function() { + stateSeederInstance = new buildfire.components.aiStateSeeder({ + generateOptions: { + userMessage: `List sample coupons for a new [Optics Shop]`, + maxRecords: 5, + systemMessage: + "listImage is an 1080x720 image URL related to title and the list type, use source.unsplash.com for images, URL should not have premium_photo or source.unsplash.com/random.", + jsonTemplate: jsonTemplate, + callback: handleAIReq.bind(this, false), + hintText: 'Replace values between brackets to match your requirements.', + }, + importOptions: { + jsonTemplate: jsonTemplate, + sampleCSV: "Save 20% on Flights, Get 20% off on flight bookings with this exclusive coupon, https://source.unsplash.com/1080x720/?travel\n50% Off Hotel Bookings, Enjoy a 50% discount on hotel reservations using this limited-time coupon, https://source.unsplash.com/1080x720/?hotel\nCar Rental Special Offer, Rent a car for 7 days and pay for only 5 days with this coupon code, https://source.unsplash.com/1080x720/?car\nAdventure Tour Promo, Book an adventure tour and receive a free equipment rental worth $50 using this coupon, https://source.unsplash.com/1080x720/?adventure", + maxRecords: 5, + hintText: 'Each row should start with a Coupon title, Summary, and Image URL', + systemMessage: 'listImage is an image URL, summary and listImage are optional', + callback: handleAIReq.bind(this, true), + }, + }).smartShowEmptyState(); + return true; + }, + } + }]) })(window.angular, window.buildfire); \ No newline at end of file diff --git a/control/content/assets/css/jquery-ui.css b/control/content/assets/css/jquery-ui.css index e827fb1..b254cad 100644 --- a/control/content/assets/css/jquery-ui.css +++ b/control/content/assets/css/jquery-ui.css @@ -1273,5 +1273,6 @@ body .ui-tooltip { } .draggable-list-view.wide-list{ - overflow: visible !important; + /* overflow: visible !important; */ + overflow-y: scroll; } \ No newline at end of file diff --git a/control/content/assets/css/sortable.css b/control/content/assets/css/sortable.css index 49456b6..4b9c30b 100644 --- a/control/content/assets/css/sortable.css +++ b/control/content/assets/css/sortable.css @@ -10,9 +10,7 @@ .draggable-list-view .d-item.square-icon .media-holder{ margin-right: .5rem; object-fit: cover; -} -.draggable-list-view .d-item.square-icon .media-holder { - object-fit: cover; + min-width: 50px; } .draggable-list-view.no-media .d-item .copy{ display: flex; @@ -91,6 +89,7 @@ html[buildfire] .d-item .btn-edit-icon:hover::after{ .media-holder.pull-left > img { object-fit: cover !important; + min-width: 50px; } .draggable-list-view.carousel-items .d-item span.title { @@ -103,6 +102,13 @@ html[buildfire] .d-item .btn-edit-icon:hover::after{ top: 3px } +.draggable-list-view .d-item.double-line .copy .delete-icon-cont { + display: flex; +} +.draggable-list-view .d-item.double-line .overflow-hidden { + overflow: hidden; +} + a.text-primary.text { top: -15px !important } diff --git a/control/content/controllers/content.home.controller.js b/control/content/controllers/content.home.controller.js index bbb1e77..36fd2f7 100644 --- a/control/content/controllers/content.home.controller.js +++ b/control/content/controllers/content.home.controller.js @@ -3,10 +3,15 @@ (function (angular, buildfire) { angular .module('couponPluginContent') - .controller('ContentHomeCtrl', ['$scope', '$timeout', 'TAG_NAMES', 'SORT', 'SORT_FILTER', 'STATUS_CODE', 'DataStore', 'LAYOUTS', 'Buildfire', 'Modals', 'RankOfLastFilter', 'RankOfLastItem', '$csv', 'Utils', '$rootScope', 'PluginEvents', - function ($scope, $timeout, TAG_NAMES, SORT, SORT_FILTER, STATUS_CODE, DataStore, LAYOUTS, Buildfire, Modals, RankOfLastFilter, RankOfLastItem, $csv, Utils, $rootScope, PluginEvents) { - + .controller('ContentHomeCtrl', ['$scope', '$timeout', 'TAG_NAMES', 'SORT', 'SORT_FILTER', 'STATUS_CODE', 'DataStore', 'LAYOUTS', 'Buildfire', 'Modals', 'RankOfLastFilter', 'RankOfLastItem', '$csv', 'Utils', '$rootScope', 'PluginEvents', 'StateSeeder', '$filter', + function ($scope, $timeout, TAG_NAMES, SORT, SORT_FILTER, STATUS_CODE, DataStore, LAYOUTS, Buildfire, Modals, RankOfLastFilter, RankOfLastItem, $csv, Utils, $rootScope, PluginEvents, StateSeeder, $filter) { var ContentHome = this; + let stateSeeder; + $rootScope.$watch('showEmptyState', function(newValue, oldValue) { + if ((typeof newValue === 'undefined' || newValue == true) && !stateSeeder) { + stateSeeder = StateSeeder.initStateSeeder(); + } + }) ContentHome.searchValue = ""; ContentHome.filter = null; ContentHome.isBusy = true; @@ -799,9 +804,34 @@ "limit": "50" }; - ContentHome.loadMoreItems = function (str) { - console.log("------------------>>>>>>>>>>>>>>>>>>>>", str) + ContentHome.reloadCoupons = function () { + ContentHome.isBusy = true; + Buildfire.datastore.search(ContentHome.searchOptionsForItems, TAG_NAMES.COUPON_ITEMS, function (err, result) { + if (err) { + Buildfire.spinner.hide(); + return console.error('-----------err in getting list-------------', err); + } + if (result.length <= SORT._limit) {// to indicate there are more + ContentHome.noMore = true; + Buildfire.spinner.hide(); + } else { + result.pop(); + ContentHome.searchOptionsForItems.skip = ContentHome.searchOptionsForItems.skip + SORT._limit; + ContentHome.noMore = false; + } + ContentHome.items = result; + ContentHome.isBusy = false; + $rootScope.reloadCoupons = false; + $scope.$digest(); + if (!ContentHome.items.length) { + $rootScope.showEmptyState = true; + } else { + $rootScope.showEmptyState = false; + } + }) + } + ContentHome.loadMoreItems = function (str) { Buildfire.spinner.show(); if (ContentHome.busy) { return; @@ -845,7 +875,11 @@ ContentHome.items = ContentHome.items ? ContentHome.items.concat(result) : result; Buildfire.datastore.search(searchOptionsFilterForItemList, TAG_NAMES.COUPON_CATEGORIES, function (err, resultFilter) { - + if (!ContentHome.items.length) { + $rootScope.showEmptyState = true; + } else { + $rootScope.showEmptyState = false; + } if (err) { Buildfire.spinner.hide(); return console.error('-----------err in getting list-------------', err); @@ -1323,6 +1357,7 @@ * Go pull any previously saved data * */ var init = function () { + console.log('$scope ====================>', $scope) var success = function (result) { console.info('Init success result:', result); ContentHome.data = result.data; @@ -1387,6 +1422,11 @@ return ContentHome.filter; }, updateItemsWithDelay, true); - + $rootScope.$watch('reloadCoupons', function(newValue, oldValue) { + if (newValue) { + ContentHome.reloadCoupons(); + } + }) }]); + })(window.angular, window.buildfire); diff --git a/control/content/controllers/content.item.controller.js b/control/content/controllers/content.item.controller.js index bffb053..b292ccd 100644 --- a/control/content/controllers/content.item.controller.js +++ b/control/content/controllers/content.item.controller.js @@ -495,8 +495,9 @@ } //# + $rootScope.showEmptyState = false; buildfire.messaging.sendMessageToWidget({}); - Location.goToHome(); + $location.path('home'); }; ContentItem.setLocation = function (data) { console.log( diff --git a/control/content/index.html b/control/content/index.html index 36f2eed..957238f 100755 --- a/control/content/index.html +++ b/control/content/index.html @@ -10,9 +10,10 @@ + + - diff --git a/control/content/templates/home.html b/control/content/templates/home.html index d1e98ef..61227c3 100644 --- a/control/content/templates/home.html +++ b/control/content/templates/home.html @@ -138,35 +138,33 @@

Coupons

Loading...

+
-
- +
-
-
- {{item.data.title}} - {{item.data.summary}} -
- -
+
+ {{item.data.title}} + {{item.data.summary}} +
+ +
Expires On
{{item.data.expiresOn | date :'mediumDate'}}
+ class="title pull-left ellipsis" ng-show="item.data.expiresOn!=''" ng-class="{'expired-item' : (ContentHome.currentDateTimestamp - item.data.expiresOn) > 1}">Expires On
{{item.data.expiresOn | date :'mediumDate'}} Expires On
No Expiration
+ class="title pull-left ellipsis" ng-hide="item.data.expiresOn!=''">Expires On
No Expiration
-
+
-
+
diff --git a/widget/app.js b/widget/app.js index b91aebb..b6da87b 100644 --- a/widget/app.js +++ b/widget/app.js @@ -327,7 +327,7 @@ break; case 'ImportCSV': msg.importing ? $rootScope.importingCSV = true : $rootScope.importingCSV = false; - if(!$rootScope.importingCSV) window.location.reload(); + if(!$rootScope.importingCSV) window.location.href = window.location.href; default: ViewStack.popAllViews(true); } diff --git a/widget/controllers/widget.home.controller.js b/widget/controllers/widget.home.controller.js index be20095..8349525 100644 --- a/widget/controllers/widget.home.controller.js +++ b/widget/controllers/widget.home.controller.js @@ -146,7 +146,6 @@ }; WidgetHome.init = function (cb) { - Buildfire.spinner.show(); var success = function (result) { Buildfire.spinner.hide(); if (result && result.data) {