247247 buildfire . analytics . unregisterEvent ( 'coupon_item_view_' + key ) ;
248248 }
249249 } ;
250- } ] ) ; ;
250+ } ] ) . factory ( 'StateSeeder' , [ 'TAG_NAMES' , 'DataStore' , 'RankOfLastItem' , '$rootScope' , '$timeout' , function ( TAG_NAMES , DataStore , RankOfLastItem , $rootScope , $timeout ) {
251+ let itemsList ;
252+ let stateSeederInstance ;
253+ $rootScope . oldCouponsIds = [ ] ;
254+ let jsonTemplate = {
255+ items : [
256+ {
257+ title : "" ,
258+ summary : "" ,
259+ listImage : "" ,
260+ } ,
261+ ] ,
262+ } ;
263+ let handleAIReq = function ( isImport , err , data ) {
264+ if (
265+ err ||
266+ ! data ||
267+ typeof data !== "object" ||
268+ ! Object . keys ( data ) . length || ! data . data || ! data . data . items || ! data . data . items . length
269+ ) {
270+ return buildfire . dialog . toast ( {
271+ message : "Bad AI request, please try changing your request." ,
272+ type : "danger" ,
273+ } ) ;
274+ }
275+ itemsList = data . data . items ;
276+ //Check image URLs
277+ let coupons = itemsList . map ( item => {
278+ return new Promise ( ( resolve , reject ) => {
279+ checkNotFoundImages ( item . listImage ?? "" , isImport ) . then ( res => {
280+ if ( res . isValid ) {
281+ item . listImage = res . newURL ;
282+ resolve ( item ) ;
283+ } else {
284+ reject ( 'image URL not valid' ) ;
285+ }
286+ } )
287+ } )
288+ } )
289+
290+ // Check image URLs
291+ Promise . allSettled ( coupons ) . then ( results => {
292+ itemsList = [ ] ;
293+ results . forEach ( res => {
294+ if ( res . status == 'fulfilled' ) {
295+ const coupon = res . value ;
296+ if ( coupon ) {
297+ itemsList . push ( coupon ) ;
298+ }
299+ }
300+ } )
301+ if ( ! itemsList . length ) {
302+ stateSeederInstance ?. requestResult ?. complete ( ) ;
303+ return buildfire . dialog . toast ( {
304+ message : "Bad AI request, please try changing your request." ,
305+ type : "danger" ,
306+ } ) ;
307+ }
308+
309+ // reset old data
310+ checkOldData ( ) . then ( ( ) => {
311+ // save new data
312+ buildfire . messaging . sendMessageToWidget ( { type : "ImportCSV" , importing : true } ) ;
313+ let promises = itemsList . map ( ( item , i ) => {
314+ return new Promise ( ( resolve , reject ) => {
315+ itemsList [ i ] = _applyDefaults ( itemsList [ i ] ) ;
316+ RankOfLastItem . setRank ( itemsList [ i ] . rank ) ;
317+ DataStore . insert ( itemsList [ i ] , TAG_NAMES . COUPON_ITEMS ) . then ( ( res ) => {
318+ if ( res ) {
319+ $rootScope . oldCouponsIds . push ( res . id ) ;
320+ itemsList [ i ] . deepLinkId = res . id ,
321+ itemsList [ i ] . deepLinkUrl = buildfire . deeplink . createLink ( { id : res . id } )
322+ new Deeplink ( {
323+ deeplinkId : res . id ,
324+ name : res . data . title ,
325+ imageUrl : res . data . listImage ? res . data . listImage : null ,
326+ deeplinkData : {
327+ id : res . id ,
328+ }
329+ } ) . save ( ( err , deepLinkData ) => {
330+ itemsList [ i ] . deepLinkId = deepLinkData . deeplinkId ;
331+ DataStore . update ( res . id , itemsList [ i ] , TAG_NAMES . COUPON_ITEMS ) . then ( ( ) => {
332+ } )
333+ resolve ( ) ;
334+ } ) ;
335+ }
336+ } )
337+
338+ } ) ;
339+ } )
340+ Promise . allSettled ( promises ) . then ( ( ) => {
341+ $timeout ( ( ) => {
342+ buildfire . messaging . sendMessageToWidget ( { type : "ImportCSV" , importing : false } ) ;
343+ $rootScope . reloadCoupons = true ;
344+ } )
345+ $rootScope . reloadCoupons = true ;
346+ } ) . catch ( err => console . warn ( 'error while saving data: ' , err ) )
347+
348+ } )
349+ stateSeederInstance ?. requestResult ?. complete ( ) ;
350+ } )
351+ }
352+
353+ // UTILITIES
354+ let _applyDefaults = function ( item ) {
355+ if ( item . title ) {
356+ return {
357+ title : item . title ,
358+ summary : item . summary || "" ,
359+ listImage : item . listImage || "" ,
360+ startOn : Date . now ( ) ,
361+ expiresOn : Math . trunc ( Date . now ( ) + Math . random ( ) * 8640000000 ) ,
362+ links : [ ] ,
363+ preRedemptionText : "Redeem Now" ,
364+ postRedemptionText : "Coupon Redeemed" ,
365+ carouselImages : item . listImage ? [
366+ {
367+ "action" : "noAction" ,
368+ "iconUrl" : item . listImage ,
369+ "title" : "image"
370+ }
371+ ] : [ ] ,
372+ rank : RankOfLastItem . getRank ( ) ? RankOfLastItem . getRank ( ) + 10 : 10 ,
373+ addressTitle : "" ,
374+ location : {
375+ addressTitle : "" ,
376+ coordinates : {
377+ lat : "" ,
378+ lng : ""
379+ }
380+ } ,
381+ Categories : [ ] ,
382+ reuseAfterInMinutes : - 1 ,
383+ dateCreated : Date . now ( ) ,
384+ deepLinkUrl : '' , // must have an id from datatore
385+ deepLinkId : '' , //same as item id
386+ SelectedCategories : [ ] ,
387+ }
388+ }
389+ return null
390+ }
391+
392+ let checkNotFoundImages = function ( url , isImport ) {
393+ const optimisedURL = url . replace ( '1080x720' , '100x100' ) ;
394+ return new Promise ( ( resolve ) => {
395+ if ( url . includes ( "http" ) ) {
396+ const xhr = new XMLHttpRequest ( ) ;
397+ xhr . open ( "GET" , optimisedURL ) ;
398+ xhr . onerror = ( error ) => {
399+ console . warn ( 'provided URL is not a valid image' , error ) ;
400+ resolve ( { isValid : true , newURL : isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png' } ) ;
401+ }
402+ xhr . onload = ( ) => {
403+ if ( xhr . responseURL . includes ( 'source-404' ) || xhr . status == 404 ) {
404+ return resolve ( { isValid : true , newURL : isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png' } ) ;
405+ } else {
406+ return resolve ( { isValid : true , newURL : xhr . responseURL . replace ( 'h=100' , 'h=720' ) . replace ( 'w=100' , 'w=1080' ) } ) ;
407+ }
408+ } ;
409+ xhr . send ( ) ;
410+ } else resolve ( { isValid : true , newURL : isImport ? null : 'https://dummyimage.com/300x300/d7dbde/ffffff.png' } ) ;
411+ } ) ;
412+ } ; ``
413+
414+ let checkOldData = function ( ) {
415+ return new Promise ( resolve => {
416+ if ( stateSeederInstance . requestResult . resetData ) {
417+ $rootScope . oldCouponsIds . forEach ( id => {
418+ Deeplink . deleteById ( id ) ;
419+ } )
420+ $rootScope . oldCouponsIds = [ ] ;
421+ DataStore . save ( [ ] , TAG_NAMES . COUPON_ITEMS ) . then ( ( ) => {
422+ resolve ( ) ;
423+ } )
424+ } else {
425+ resolve ( ) ;
426+ }
427+ } )
428+ }
429+
430+ return {
431+ initStateSeeder : function ( ) {
432+ stateSeederInstance = new buildfire . components . aiStateSeeder ( {
433+ generateOptions : {
434+ userMessage : `List sample coupons for a new [Optics Shop]` ,
435+ maxRecords : 5 ,
436+ systemMessage :
437+ "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." ,
438+ jsonTemplate : jsonTemplate ,
439+ callback : handleAIReq . bind ( this , false ) ,
440+ hintText : 'Replace values between brackets to match your requirements.' ,
441+ } ,
442+ importOptions : {
443+ jsonTemplate : jsonTemplate ,
444+ 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" ,
445+ maxRecords : 5 ,
446+ hintText : 'Each row should start with a Coupon title, Summary, and Image URL' ,
447+ systemMessage : 'listImage is an image URL, summary and listImage are optional' ,
448+ callback : handleAIReq . bind ( this , true ) ,
449+ } ,
450+ } ) . smartShowEmptyState ( ) ;
451+ return true ;
452+ } ,
453+ }
454+ } ] )
251455} ) ( window . angular , window . buildfire ) ;
0 commit comments