164164 return deferred . promise ;
165165 }
166166 } ;
167- } ] )
167+ } ] ) . factory ( 'StateSeeder' , [ 'Context' , 'LoyaltyAPI' , '$rootScope' , 'Buildfire' , '$timeout' , function ( Context , LoyaltyAPI , $rootScope , Buildfire , $timeout ) {
168+ let itemsList ;
169+ let orderedItems = [ ] ;
170+ let currentUser ;
171+ let currentContext ;
172+ let stateSeederInstance ;
173+ let generateJSONTemplate = {
174+ items : [
175+ {
176+ title : "" ,
177+ pointsToRedeem : 0 ,
178+ description : "" ,
179+ listImage : "" ,
180+ } ,
181+ ] ,
182+ } ;
183+ let importJSONTemplate = {
184+ items : [
185+ {
186+ title : "" ,
187+ pointsToRedeem : 0 ,
188+ pointsPerItem : 0 ,
189+ description : "" ,
190+ listImage : "" ,
191+ } ,
192+ ] ,
193+ } ;
194+ let handleAIReq = function ( isImport , err , data ) {
195+ if (
196+ err ||
197+ ! data ||
198+ typeof data !== "object" ||
199+ ! Object . keys ( data ) . length || ! data . data || ! data . data . items || ! data . data . items . length
200+ ) {
201+ return buildfire . dialog . toast ( {
202+ message : "Bad AI request, please try changing your request." ,
203+ type : "danger" ,
204+ } ) ;
205+ }
206+ let initPromises = [
207+ new Promise ( ( resolve , reject ) => {
208+ getCurrentUser ( ) . then ( ( user ) => {
209+ currentUser = user ;
210+ resolve ( ) ;
211+ } ) . catch ( err => reject ( err ) )
212+ } ) ,
213+ new Promise ( ( resolve , reject ) => {
214+ Context . getContext ( ) . then ( context => {
215+ currentContext = context ;
216+ resolve ( ) ;
217+ } ) . catch ( err => reject ( err ) )
218+ } )
219+ ]
220+ Promise . all ( initPromises ) . then ( ( ) => {
221+ itemsList = data . data . items ;
222+ //Check image URLs
223+ let items = itemsList . map ( ( item , i ) => {
224+ return new Promise ( ( resolve , reject ) => {
225+ elimanateNotFoundImages ( item . listImage ) . then ( res => {
226+ if ( res . isValid ) {
227+ item . listImage = res . newURL ;
228+ item . order = i ;
229+ orderedItems . push ( item ) ;
230+ resolve ( item ) ;
231+ } else {
232+ reject ( 'image URL not valid' ) ;
233+ }
234+ } )
235+ } )
236+ } )
237+
238+ Promise . allSettled ( items ) . then ( results => {
239+ itemsList = [ ] ;
240+ results . forEach ( res => {
241+ if ( res . status == 'fulfilled' ) {
242+ const item = res . value ;
243+ if ( item ) {
244+ itemsList . push ( item ) ;
245+ }
246+ }
247+ } )
248+ if ( ! itemsList . length ) {
249+ stateSeederInstance ?. requestResult ?. complete ( ) ;
250+ return buildfire . dialog . toast ( {
251+ message : isImport ? "Each row must have a valid image URL." : "Bad AI request, please try changing your request." ,
252+ type : "danger" ,
253+ } ) ;
254+ }
255+
256+ // reset old data
257+ deleteAll ( ) . then ( ( ) => {
258+ // save new data
259+ let promises = itemsList . map ( ( item , i ) => {
260+ return new Promise ( ( resolve , reject ) => {
261+ itemsList [ i ] = _applyDefaults ( item ) ;
262+ LoyaltyAPI . addReward ( itemsList [ i ] ) . then ( result => {
263+ console . info ( 'Saved data result: ' , result ) ;
264+ itemsList [ i ] . deepLinkUrl = Buildfire . deeplink . createLink ( { id : result . _id } ) ;
265+ itemsList [ i ] = Object . assign ( itemsList [ i ] , result ) ;
266+ resolve ( ) ;
267+ } )
268+ . catch ( err => {
269+ console . error ( 'Error while saving data : ' , err ) ;
270+ resolve ( 'Error while saving data : ' , err ) ;
271+ } )
272+ } )
273+ } )
274+ Promise . allSettled ( promises ) . then ( res => {
275+ if ( isImport ) {
276+ let sortedItems = orderedItems . sort ( ( a , b ) => a . order - b . order ) ;
277+ let sortedIds = [ ] ;
278+ LoyaltyAPI . getRewards ( currentContext . instanceId ) . then ( results => {
279+ sortedItems . forEach ( item => {
280+ if ( results ) {
281+ results . forEach ( result => {
282+ if ( item . title == result . title && item . listImage == result . listImage ) {
283+ sortedIds . push ( result . _id ) ;
284+ }
285+ } )
286+ }
287+ } )
288+ results . forEach ( result => {
289+ if ( ! sortedIds . includes ( result . _id ) )
290+ sortedIds . push ( result . _id ) ;
291+ } )
292+ const data = {
293+ appId : currentContext . appId ,
294+ loyaltyUnqiueId : currentContext . instanceId ,
295+ userToken : currentUser && currentUser . userToken ,
296+ auth : currentUser && currentUser . auth ,
297+ loyaltyRewardIds : sortedIds
298+ }
299+ LoyaltyAPI . sortRewards ( data ) . finally ( ( ) => {
300+ $timeout ( ( ) => {
301+ sortedItems = [ ] ;
302+ orderedItems = [ ] ;
303+ $rootScope . reloadRewards = true ;
304+ buildfire . messaging . sendMessageToWidget ( {
305+ type : 'refresh'
306+ } ) ;
307+ stateSeederInstance ?. requestResult ?. complete ( ) ;
308+ } )
309+ } ) ;
310+ } ) ;
311+ } else {
312+ $timeout ( ( ) => {
313+ $rootScope . reloadRewards = true ;
314+ orderedItems = [ ] ;
315+ buildfire . messaging . sendMessageToWidget ( {
316+ type : 'refresh'
317+ } ) ;
318+ stateSeederInstance ?. requestResult ?. complete ( ) ;
319+ } )
320+ }
321+ } )
322+ } ) . catch ( err => console . warn ( 'old data delete error' , err ) ) ;
323+ } )
324+ } ) . catch ( err => {
325+ console . error ( err ) ;
326+ stateSeederInstance ?. requestResult ?. complete ( ) ;
327+ } )
328+ }
329+
330+ // UTILITIES
331+ let _applyDefaults = function ( item ) {
332+ if ( item . title ) {
333+ const points = checkPoints ( item . pointsToRedeem , item . pointsPerItem ) ;
334+ return {
335+ title : item . title ,
336+ pointsToRedeem : points . pointsToRedeem ,
337+ description : item . description || "" ,
338+ listImage : item . listImage || "" ,
339+ pointsPerItem : points . pointsPerItem ,
340+ appId : currentContext . appId ,
341+ loyaltyUnqiueId : currentContext . instanceId ,
342+ userToken : currentUser && currentUser . userToken ,
343+ auth : currentUser && currentUser . auth ,
344+ }
345+ }
346+ return null
347+ }
348+
349+ let checkPoints = function ( pointsToRedeem , pointsPerItem ) {
350+ let points = {
351+ pointsToRedeem : 0 ,
352+ pointsPerItem : 0 ,
353+ } ;
354+ if ( pointsToRedeem && pointsToRedeem > 0 ) {
355+ points . pointsToRedeem = pointsToRedeem ;
356+ } else {
357+ points . pointsToRedeem = 100 ;
358+ }
359+
360+ if ( pointsPerItem && pointsPerItem > 0 ) {
361+ points . pointsPerItem = pointsPerItem ;
362+ } else {
363+ points . pointsPerItem = Math . ceil ( points . pointsToRedeem * 0.1 ) ;
364+ }
365+ return points ;
366+ }
367+
368+ let elimanateNotFoundImages = function ( url ) {
369+ const optimisedURL = url . replace ( '1080x720' , '100x100' ) ;
370+ return new Promise ( ( resolve ) => {
371+ if ( url . includes ( "http" ) ) {
372+ const xhr = new XMLHttpRequest ( ) ;
373+ xhr . open ( "GET" , optimisedURL ) ;
374+ xhr . onerror = ( error ) => {
375+ console . warn ( 'provided URL is not a valid image' , error ) ;
376+ resolve ( { isValid : false , newURL : null } ) ;
377+ }
378+ xhr . onload = ( ) => {
379+ if ( xhr . responseURL . includes ( 'source-404' ) || xhr . status == 404 ) {
380+ return resolve ( { isValid : false , newURL : null } ) ;
381+ } else {
382+ return resolve ( { isValid : true , newURL : xhr . responseURL . replace ( 'h=100' , 'h=720' ) . replace ( 'w=100' , 'w=1080' ) } ) ;
383+ }
384+ } ;
385+ xhr . send ( ) ;
386+ } else resolve ( false ) ;
387+ } ) ;
388+ } ;
389+
390+ let deleteAll = function ( ) {
391+ const data = {
392+ userToken : currentUser . userToken ,
393+ auth : currentUser . auth ,
394+ appId : currentContext . appId
395+ } ;
396+ return new Promise ( resolve => {
397+ if ( stateSeederInstance . requestResult . resetData ) {
398+ LoyaltyAPI . getRewards ( currentContext . instanceId ) . then ( items => {
399+ const promises = items . map ( ( item ) => deleteItem ( item . _id , data ) ) ;
400+ resolve ( Promise . all ( promises ) ) ;
401+ } ) . catch ( err => console . warn ( 'old data get error' , err ) ) ;
402+ }
403+ else {
404+ resolve ( ) ;
405+ }
406+ } )
407+ }
408+
409+ let deleteItem = function ( itemId , data ) {
410+ return new Promise ( ( resolve , reject ) => {
411+ LoyaltyAPI . removeReward ( itemId , data ) . then ( res => {
412+ Deeplink . deleteById ( itemId ) ;
413+ resolve ( res ) ;
414+ } ) . catch ( err => {
415+ if ( err ) reject ( err ) ;
416+ } )
417+ } ) ;
418+ }
419+
420+ let getCurrentUser = function ( ) {
421+ return new Promise ( resolve => {
422+ buildfire . auth . getCurrentUser ( function ( err , user ) {
423+ if ( user && user . _cpUser ) {
424+ resolve ( user . _cpUser ) ;
425+ }
426+ } ) ;
427+ } )
428+ }
429+
430+ return {
431+ initStateSeeder : function ( ) {
432+ stateSeederInstance = new buildfire . components . aiStateSeeder ( {
433+ generateOptions : {
434+ userMessage : `Generate a sample of redeemable items for a new [business-type].` ,
435+ maxRecords : 5 ,
436+ systemMessage :
437+ 'listImage URL related to title and the list type. use source.unsplash.com for image URL, URL should not have premium_photo or source.unsplash.com/random, cost to redeem which is a number greater than zero and less than 100, return description as HTML.' ,
438+ jsonTemplate : generateJSONTemplate ,
439+ callback : handleAIReq . bind ( this , false ) ,
440+ hintText : 'Replace values between brackets to match your requirements.' ,
441+ } ,
442+ importOptions : {
443+ jsonTemplate : importJSONTemplate ,
444+ sampleCSV : "Hotel Voucher, 50, 10, Redeem this voucher for a one-night stay at a luxurious hotel of your choice, https://source.unsplash.com/featured/?hotel\nFlight Upgrade, 30, 15, Upgrade your economy class ticket to business class, https://source.unsplash.com/featured/?flight\nCity Tour, 20, 5, Explore the city with a guided tour that covers all the major attractions and landmarks, https://source.unsplash.com/featured/?city\nAdventure Activity, 80, 30, Embark on an adrenaline-pumping adventure activity such as bungee jumping or skydiving, https://source.unsplash.com/featured/?adventure" ,
445+ maxRecords : 15 ,
446+ hintText : 'Each row should start with a loyalty item title, cost to redeem, points earned, description, and image URL.' ,
447+ systemMessage : `` ,
448+ callback : handleAIReq . bind ( this , true ) ,
449+ } ,
450+ } ) . smartShowEmptyState ( ) ;
451+ } ,
452+ }
453+ } ] )
168454} ) ( window . angular , window . buildfire ) ;
0 commit comments