{"version":3,"file":"app-c8cda2b9.981ca8458d389c9a476a.bundle.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AAEA;AACA;AAAA;AACA;AACA;;AAGA;AACA;AAyBA;AAbA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAaA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AAEA;;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;;AAGA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAFA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;AC9RA;AACA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;;;;;;;;;;;;;;AC1GA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;AChDA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AC9CA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAFA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAIA;AAEA;AAMA;AALA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAKA;AAJA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAMA;AALA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;ACtFA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AAGA;AAEA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;AC9EA;;AAEA;AACA;AAGA;AAGA;AAGA;;AAKA;AACA;AAMA;AALA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAGA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AAAA;AACA;AAGA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;ACzEA;AACA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;AADA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAGA;AACA;;;;;;;;;;;;;;;;ACrEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1DA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAKA;AACA;AACA;;AAEA;AACA;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AAAA;;AAIA;AACA;AAsBA;AATA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAIA;AAHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAAA;AAAA;AAAA;AACA;AAEA;;AAGA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjPA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AACA;;AAEA;AACA;AACA;AAMA;;AAQA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;;AAIA;AACA;AAyBA;AAdA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAFA;AAAA;AACA;AACA;AACA;AAAA;AAAA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAKA;AAIA;AAIA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAGA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChcA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;;AAIA;AACA;AAeA;AAJA;AAAA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAMA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;AC/GA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAGA;AACA;AAUA;AAJA;AAAA;AACA;AAIA;AACA;AACA;;AAGA;AACA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7DA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;;AAIA;AACA;AAkBA;AARA;AAAA;AACA;AACA;AACA;AACA;AACA;AAWA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAGA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAGA;AAEA;AASA;AAOA;AACA;AAEA;AAQA;AAKA;AAEA;AAMA;AAIA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAMA;AAGA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;;AAGA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;AClvBA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAGA;AACA;AACA;AACA;AAGA;AAFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;;;;;;;;;;;;;;AC7CA;;AAEA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BA;;AAEA;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAIA;;AAIA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;;AAIA;AACA;AAwBA;AAbA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAGA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAAA;AAAA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAKA;AACA;AACA;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAIA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;AClfA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAGA;AACA;AAeA;AANA;AAAA;AACA;AACA;AACA;AAUA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACA;;AAGA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;;;;;;;;;;;ACnPA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA;;;;;;;;;;;ACHA;AACA;AACA;AACA","sources":["webpack://latinera/./sources/screens/private/activities.js","webpack://latinera/./sources/screens/private/activities/add-analysis-activity-from-task-activity-to-activities.js","webpack://latinera/./sources/screens/private/activities/create-analysis-activity.js","webpack://latinera/./sources/screens/private/activities/create-task-activity.js","webpack://latinera/./sources/screens/private/activities/initialize-activities-list-sortable.js","webpack://latinera/./sources/screens/private/activities/move-activity-within-activities.js","webpack://latinera/./sources/screens/private/activities/move-activity.js","webpack://latinera/./sources/screens/private/activities/remove-analysis-activity-from-activities.js","webpack://latinera/./sources/screens/private/activities/remove-analysis-activity-from-task-activity.js","webpack://latinera/./sources/screens/private/activity.js","webpack://latinera/./sources/screens/private/checkout.js","webpack://latinera/./sources/screens/private/dashboard.js","webpack://latinera/./sources/screens/private/invite-user.js","webpack://latinera/./sources/screens/private/offer.js","webpack://latinera/./sources/screens/private/offer/get-offer-by-key.js","webpack://latinera/./sources/screens/private/offer/get-offer-discounts-data.js","webpack://latinera/./sources/screens/private/payment-status.js","webpack://latinera/./sources/screens/private/settings.js","webpack://latinera/./sources/screens/private/activities.html","webpack://latinera/./sources/screens/private/activity.html","webpack://latinera/./sources/screens/private/checkout.html","webpack://latinera/./sources/screens/private/dashboard.html","webpack://latinera/./sources/screens/private/invite-user.html","webpack://latinera/./sources/screens/private/offer.html","webpack://latinera/./sources/screens/private/payment-status.html","webpack://latinera/./sources/screens/private/settings.html"],"sourcesContent":["// Define the view-model for the \"activities\" screen\n\n// Import library modules\nimport { DialogService } from \"aurelia-dialog\";\nimport { inject } from \"aurelia-framework\";\nimport { Router } from \"aurelia-router\";\n\n// Import utility modules\nimport { setEventHandlers, unsetEventHandlers } from \"utilities/event\";\nimport { typeOf } from \"utilities/etc\";\nimport { \n computeAnalysisActivityItem,\n computeActivityType\n} from \"utilities/activity\";\n\n// Import parameter modules\nimport { runMode as defaultRunMode } from \"parameters/environment\";\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\n\n// Import service modules\nimport ActivityService from \"services/activity\";\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport IndexService from \"services/index\";\nimport NotificationService from \"services/notification\";\nimport UserService from \"services/user\";\n\n// Import method modules\nimport { createAnalysisActivity } from \"./activities/create-analysis-activity\";\nimport { createTaskActivity } from \"./activities/create-task-activity\";\nimport { moveActivity } from \"./activities/move-activity\";\nimport {\n initializeActivitiesListSortable\n} from \"./activities/initialize-activities-list-sortable\";\n\n// Define internal parameters\nconst autoscrollTimeoutMs = 100;\nconst eventsData = [\n { name: \"taskActivityDataChanged\" },\n { name: \"analysisActivityDataSaved\" },\n { name: \"deleteActivity\"},\n { name: \"runModeChanged\" }\n];\nconst scrollOptions = {\n behavior: \"smooth\", // smooth|instant|auto\n block: \"center\" // start|center|end|nearest\n};\n\n\n// Export the \"Activities\" class\n@inject(\n ActivityService,\n ApplicationService,\n DialogService,\n EventService,\n IndexService,\n NotificationService,\n Router,\n UserService\n)\nexport class Activities {\n\n // Local attributes\n activitiesData = [];\n sortActivitiesByAscendingIndex = true;\n isLoadingActivities = false;\n activitiesListSortable = null;\n showNavigateBackIcon = true;\n showScrollToTopIcon = this.sortActivitiesByAscendingIndex;\n showScrollToBottomIcon = !this.sortActivitiesByAscendingIndex;\n showAnalysisActivities = true;\n showTaskActivities = true;\n runMode = defaultRunMode;\n i18nParams = i18nParams;\n\n constructor(\n activityService,\n applicationService,\n dialogService,\n eventService,\n indexService,\n notificationService,\n router,\n userService\n ) {\n this.activityService = activityService;\n this.applicationService = applicationService;\n this.dialogService = dialogService;\n this.eventService = eventService;\n this.indexService = indexService;\n this.notificationService = notificationService;\n this.router = router;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n async activate() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"activities\",\n eventService: this.eventService,\n eventsData\n });\n\n // Load run-mode\n this.runMode = await this.applicationService.getRunMode();\n\n // Load current activities\n this.isLoadingActivities = true;\n try {\n this.activitiesData = await this.activityService.getCurrentActivities();\n } catch(error) {\n console.warn(`Error fetching current activities from service`, error);\n this.activitiesData = [];\n }\n this.isLoadingActivities = false;\n }\n\n async attached() {\n this.initializeActivitiesListSortable();\n\n // Get last opened activity element\n const lastOpenedActivityId = \n await this.activityService.getLastOpenedActivityId();\n const lastOpenedActivityElement = lastOpenedActivityId ?\n document.getElementById(lastOpenedActivityId) || null : null;\n\n // Autoscroll last openend activity or scroll to bottom element into view\n this.autoScrollTimeoutId = setTimeout(() => {\n this.autoScrollElement = \n lastOpenedActivityElement || this.scrollToBottomElement;\n if (this.autoScrollElement !== null) {\n this.autoScrollElement.scrollIntoView(scrollOptions);\n }\n }, autoscrollTimeoutMs);\n }\n\n deactivate() {\n if (this.autoScrollTimeoutId) {\n clearTimeout(this.autoScrollTimeoutId);\n }\n\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n\n // Core methods\n computeActivityType(activityData) {\n return computeActivityType(activityData);\n }\n\n initializeActivitiesListSortable() {\n return initializeActivitiesListSortable.call(this);\n }\n\n scrollToTop() {\n this.showScrollToTopIcon = false;\n this.userService.conditionallyExtendSession(); // do not await\n if (this.scrollToTopElement) {\n this.scrollToTopElement.scrollIntoView(scrollOptions);\n }\n }\n topElementScrolledIntoView() {\n this.showScrollToTopIcon = false;\n }\n topElementScrolledOutOfView() {\n this.showScrollToTopIcon = true;\n }\n\n scrollToBottom() {\n this.showScrollToBottomIcon = false;\n this.userService.conditionallyExtendSession(); // do not await\n if (this.scrollToBottomElement) {\n this.scrollToBottomElement.scrollIntoView(scrollOptions);\n }\n }\n bottomElementScrolledIntoView() {\n this.showScrollToBottomIcon = false;\n }\n bottomElementScrolledOutOfView() {\n this.showScrollToBottomIcon = true;\n }\n\n\n navigateBack() {\n try {\n this.router.navigateBack();\n } catch(error) {\n console.warn(`Error while navigating to previous screen`, error);\n return;\n }\n console.debug(\"Navigating to previous screen\");\n }\n\n toggleHelpCanvas() {\n this.userService.conditionallyExtendSession(); // do not await\n }\n\n toggleShowAnalysisActivities() {\n this.showAnalysisActivities = !this.showAnalysisActivities;\n this.userService.conditionallyExtendSession(); // do not await\n this.autoScrollTimeoutId = setTimeout(() => {\n if (this.showAnalysisActivities && this.toggleVisibilityElement) {\n this.toggleVisibilityElement.scrollIntoView(scrollOptions);\n }\n }, autoscrollTimeoutMs);\n }\n\n toggleShowTaskActivities() {\n this.showTaskActivities = !this.showTaskActivities;\n this.userService.conditionallyExtendSession(); // do not await\n this.autoScrollTimeoutId = setTimeout(() => {\n if (this.showTaskActivities && this.toggleVisibilityElement) {\n this.toggleVisibilityElement.scrollIntoView(scrollOptions);\n }\n }, autoscrollTimeoutMs);\n }\n\n async createAnalysisActivity() {\n await createAnalysisActivity.call(this);\n }\n\n async createTaskActivity() {\n await createTaskActivity.call(this);\n }\n\n async moveActivity(...args) {\n return await moveActivity.apply(this, args);\n }\n\n\n // Event handlers\n handleTaskActivityDataChangedEvent({ \n taskActivityData = null,\n activitiesIndex = -1\n }) {\n if (typeOf(activitiesIndex) !== \"number\" || activitiesIndex < -1) {\n console.warn(`Parameter \"activitiesIndex\" should be ` +\n `minus one or a non-negative index`);\n return;\n }\n if (taskActivityData && activitiesIndex >= 0) {\n this.activitiesData.splice(activitiesIndex, 1, taskActivityData);\n }\n }\n\n handleAnalysisActivityDataSavedEvent({ \n analysisActivityData = null,\n activitiesIndex = -1\n }) {\n if (typeOf(activitiesIndex) !== \"number\" || activitiesIndex < -1) {\n console.warn(`Parameter \"activitiesIndex\" should be either ` +\n `minus one or a non-negative index`);\n return;\n }\n if (analysisActivityData && activitiesIndex >= 0) {\n const analysisActivityItemData = computeAnalysisActivityItem({\n analysisActivityData\n })\n this.activitiesData.splice(activitiesIndex, 1, analysisActivityItemData);\n }\n }\n\n handleDeleteActivityEvent({ activityKey: targetActivityKey = \"\" }) {\n const activityIndex = this.activitiesData\n .findIndex(({ key: activityKey }) => activityKey === targetActivityKey);\n const activityData = this.activitiesData.at(activityIndex) || null;\n if (activityData) {\n this.activitiesData.splice(activityIndex, 1);\n\n // Show notification message\n const longActivityType = computeActivityType(activityData);\n this.notificationService.showUINotification({\n message: `notifications:${longActivityType}Deleted`,\n status: \"success\",\n group: \"private\"\n });\n }\n }\n\n handleRunModeChangedEvent({ runMode = \"dev\" }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the \"addAnalysisActivityFromTaskActivityToActivities\" activities\n// screen utility function\n\n// Import utility modules\nimport { cyrb53 as computeHash } from \"utilities/hash\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// addAnalysisActivityFromTaskActivityToActivities: async\n// returns a promise which resolves with a boolean indicating whether the\n// analysis activity located at the specified index of the specified task\n// activity has been added at the specified index whithin the activities'\n// list\nexport async function addAnalysisActivityFromTaskActivityToActivities({\n taskAnalysisActivityIndex = -1,\n taskActivityKey = \"\",\n activitiesIndex = -1\n}) {\n if (typeOf(taskAnalysisActivityIndex) !== \"number\" || \n taskAnalysisActivityIndex < 0) {\n throw new Error(`Parameter \"taskAnalysisActivityIndex\" should be ` +\n `a non-negative index`);\n } else if (typeOf(taskActivityKey) !== \"string\" || !taskActivityKey) {\n throw new Error(`Parameter \"taskActivityKey\" should be a key string`);\n } else if (typeOf(activitiesIndex) !== \"number\" || activitiesIndex < -1) {\n throw new Error(`Parameter \"activitiesIndex\" should be either ` +\n `minus one or a non-negative index`);\n } else if(!this.activityService) {\n throw new Error(`Missing required \"activity\" service`);\n } else if(!this.indexService) {\n throw new Error(`Missing required \"index\" service`);\n } else if(!this.notificationService) {\n throw new Error(`Missing required \"notification\" service`);\n }\n\n const taskActivityData = await this.activityService.getActivityItemByKey({\n activityKey: taskActivityKey\n });\n if (!taskActivityData) {\n console.warn(`Could not find task activity \"${taskActivityKey}\"`);\n return false;\n }\n const taskAnalysisActivityData =\n taskActivityData.task.activities[taskAnalysisActivityIndex];\n if (!taskAnalysisActivityData) {\n console.warn(`Could not find task analysis activity ` +\n `at index \"${taskAnalysisActivityIndex}\" ` +\n `of task activity \"${taskActivityKey}\"`);\n return false;\n }\n\n const { key: taskAnalysisActivityKey } = taskAnalysisActivityData;\n const nextActivityIndex = this.sortActivitiesByAscendingIndex ?\n activitiesIndex : activitiesIndex - 1;\n const nextActivityData = this.activitiesData[nextActivityIndex];\n const { key: nextActivityKey } = nextActivityData || {};\n let newAnalysisActivityData;\n try {\n newAnalysisActivityData = await this.activityService.useActivity({\n activityKey: taskAnalysisActivityKey,\n nextActivityKey // possibly undefined\n });\n } catch(error) {\n console.error(`Error adding analysis activity \"${taskAnalysisActivityKey}\" ` +\n `from task activity \"${taskActivityKey}[${taskAnalysisActivityIndex}]\" ` +\n `to activities[${activitiesIndex}]`);\n console.error(error);\n return false;\n }\n this.activitiesData.splice(activitiesIndex, 0, newAnalysisActivityData);\n\n // Update array of current activities on index\n const cacheKey = computeHash(`current-activities`);\n const cacheData = await this.indexService.getDocumentByKey({ \n collectionName: \"keyvalues\", \n documentKey: cacheKey\n });\n const { value: currentActivitiesKeys = [] } = cacheData || {};\n const { key: newAnalysisActivityKey } = newAnalysisActivityData;\n currentActivitiesKeys.splice(activitiesIndex, 0, newAnalysisActivityKey);\n try {\n await this.indexService.saveDocument({\n collectionName: \"keyvalues\", \n documentData: {\n key: cacheKey,\n value: currentActivitiesKeys\n }\n });\n } catch(error) {\n console.warn(`Error adding analysis activity \"${taskAnalysisActivityKey}\" ` +\n `from task activity \"${taskActivityKey}[${taskAnalysisActivityIndex}]\" ` +\n `to activities[${activitiesIndex}]`);\n return false;\n }\n console.info(`Added analysis activity \"${taskAnalysisActivityKey}\" ` +\n `from task activity \"${taskActivityKey}[${taskAnalysisActivityIndex}]\" ` +\n `to activities[${activitiesIndex}]`);\n\n // Display notification message\n this.notificationService.showUINotification({\n message: \"notifications:analysisMoved\",\n status: \"success\",\n group: \"private\"\n });\n\n return true;\n}\n","// Define the \"createAnalysisActivity\" activities screen method\n\n// Import dialog modules\nimport { CreateAnalysisActivity } from \"dialogs/create-analysis-activity\";\n\n\n// createAnalysisActivity: async\n// returns a promise which resolves after the user has created a new\n// analysis activity through the corresponding dialog\nexport async function createAnalysisActivity() {\n if (!this.dialogService) {\n throw new Error(`Missing required \"dialog\" service`);\n } else if (!this.notificationService) {\n throw new Error(`Missing required \"notification\" service`);\n }\n\n // Show create analysis activity dialog\n const createAnalysisActivityDialogOpenResult = \n await this.dialogService.open({\n viewModel: CreateAnalysisActivity,\n //keyboard: [\"Escape\"] // keys that close the dialog\n });\n const createAnalysisActivityDialogCloseResult =\n await createAnalysisActivityDialogOpenResult.closeResult;\n if (createAnalysisActivityDialogCloseResult.wasCancelled) {\n console.info(`User canceled the creation of a new analysis activity`);\n return;\n }\n const { \n output: createdAnalysisActivityData = null\n } = createAnalysisActivityDialogCloseResult;\n\n // Show notification message\n if (createdAnalysisActivityData) {\n this.activitiesData = \n [ ...this.activitiesData, createdAnalysisActivityData ];\n this.notificationService.showUINotification({\n message: \"notifications:analysisCreated\",\n status: \"success\",\n group: \"private\"\n });\n } else {\n this.notificationService.showUINotification({\n message: \"notifications:analysisCreationError\",\n status: \"failure\",\n group: \"private\"\n });\n }\n}\n","// Define the \"createTaskActivity\" activities screen method\n\n// Import dialog modules\nimport { CreateTaskActivity } from \"dialogs/create-task-activity\";\n\n\n// createTaskActivity: async\n// returns a promise which resolves after the user has created a new\n// task activity through the corresponding dialog\nexport async function createTaskActivity() {\n if (!this.dialogService) {\n throw new Error(`Missing required \"dialog\" service`);\n } else if (!this.notificationService) {\n throw new Error(`Missing required \"notification\" service`);\n }\n\n // Show create task activity dialog\n const createTaskDialogOpenResult = await this.dialogService.open({\n viewModel: CreateTaskActivity,\n //keyboard: [\"Escape\"] // keys that close the dialog\n });\n const createTaskDialogCloseResult =\n await createTaskDialogOpenResult.closeResult;\n if (createTaskDialogCloseResult.wasCancelled) {\n console.info(`User canceled the creation of the task activity`);\n return;\n }\n const {\n output: createdTaskActivityData = null\n } = createTaskDialogCloseResult;\n\n // Show notification message\n if (createdTaskActivityData) {\n this.activitiesData = [ ...this.activitiesData, createdTaskActivityData ];\n this.notificationService.showUINotification({\n message: \"notifications:taskCreated\",\n status: \"success\",\n group: \"private\"\n });\n } else {\n this.notificationService.showUINotification({\n message: \"notifications:taskCreationError\",\n status: \"failure\",\n group: \"private\"\n });\n }\n}\n","// Define the \"initializeActivitiesListSortable\" screen method\n\n// Import library modules\nimport Sortable from \"sortablejs\";\n\n// Import utility modules\nimport { removeElement } from \"utilities/dom\";\n\n// initializeActivitiesListSortable: sync\n// initializes the list of activities sortable element\nexport function initializeActivitiesListSortable() {\n this.activitiesListSortable = new Sortable(this.activitiesListElement, {\n group: \"activities\",\n direction: \"vertical\",\n swapThreshold: 0.75,\n animation: 150,\n easing: \"cubic-bezier(1, 0, 0, 1)\",\n draggable: \".ps-draggable-item\",\n handle: \".ps-draggable-handle\",\n ghostClass: \"ps-draggable-ghost\",\n dragClass: \"ps-draggable-drag\",\n onMove: handleOnMoveEvent.bind(this),\n onAdd: handleOnAddEvent.bind(this),\n onRemove: handleOnRemoveEvent.bind(this),\n onUpdate: handleOnUpdateEvent.bind(this)\n });\n}\n\nfunction handleOnMoveEvent({\n dragged: { id: draggedActivityId },\n related: { parentElement: { id: targetGroupId = \"\" } }\n}) {\n // prevent dragging task activities into other task activities\n return !(\n draggedActivityId.startsWith(\"taskActivity\") &&\n targetGroupId.startsWith(\"taskActivity\")\n );\n}\n\nasync function handleOnAddEvent({\n item: addedElement,\n from: { id: srcGroupId },\n to: { id: dstGroupId },\n oldIndex: srcGroupIndex,\n newIndex: dstGroupIndex\n}) {\n await this.moveActivity({\n eventName: \"add\",\n srcGroupId,\n srcGroupIndex,\n dstGroupId,\n dstGroupIndex\n });\n removeElement(addedElement);\n}\n\nasync function handleOnRemoveEvent({\n from: { id: srcGroupId },\n to: { id: dstGroupId },\n oldIndex: srcGroupIndex,\n newIndex: dstGroupIndex\n}) {\n await this.moveActivity({\n eventName: \"remove\",\n srcGroupId,\n srcGroupIndex,\n dstGroupId,\n dstGroupIndex\n });\n}\n\nasync function handleOnUpdateEvent({\n item: updatedElement,\n from: { id: srcGroupId },\n to: { id: dstGroupId },\n oldIndex: srcGroupIndex,\n newIndex: dstGroupIndex\n}) {\n await this.moveActivity({\n eventName: \"update\",\n srcGroupId,\n srcGroupIndex,\n dstGroupId,\n dstGroupIndex\n });\n removeElement(updatedElement);\n}\n","// Define the \"moveActivityWithinActivities\" screen utility function\n\n// Import utility modules\nimport { cyrb53 as computeHash } from \"utilities/hash\";\nimport { computeActivityType } from \"utilities/activity\";\nimport { moveItem } from \"utilities/array\";\n\n\n// moveActivityWithinActivities: async\n// returns a promise which resolves with a boolean indicating whether the\n// specified activity has been moved whithin the array of activities from\n// the specified source index to the specified destination index\nexport async function moveActivityWithinActivities({\n srcActivityIndex,\n dstActivityIndex\n}) {\n const srcActivityData = this.activitiesData[srcActivityIndex];\n if (!srcActivityData) {\n console.warn(`Could not find source activity ` +\n `at activities[${srcActivityIndex}]`);\n return false;\n }\n const { key: srcActivityKey } = srcActivityData;\n const srcActivityType = computeActivityType(srcActivityData);\n const activityIndexOffset = this.sortActivitiesByAscendingIndex ?\n (dstActivityIndex > srcActivityIndex ? +1 : 0) :\n (dstActivityIndex < srcActivityIndex ? -1 : 0);\n const dstNextActivityData =\n this.activitiesData[dstActivityIndex+activityIndexOffset];\n const { key: dstNextActivityKey } = dstNextActivityData || {};\n\n let newDstActivityData; // with updated index\n try {\n newDstActivityData = await this.activityService.reuseActivity({\n activityKey: srcActivityKey,\n nextActivityKey: dstNextActivityKey // possibly undefined\n });\n } catch(error) {\n console.error(`Error moving ` +\n `${srcActivityType} activity \"${srcActivityKey}\" ` +\n `within activities[${srcActivityIndex} -> ${dstActivityIndex}]\"`);\n console.error(error);\n return false;\n }\n this.activitiesData.splice(srcActivityIndex, 1);\n this.activitiesData.splice(dstActivityIndex, 0, newDstActivityData);\n\n // Update array of current activities on index\n const cacheKey = computeHash(`current-activities`);\n const cacheData = await this.indexService.getDocumentByKey({\n collectionName: \"keyvalues\",\n documentKey: cacheKey\n });\n const { value: currentActivitiesKeys = [] } = cacheData || {};\n const movedCurrentActivitiesKeys = moveItem(\n currentActivitiesKeys, srcActivityIndex, dstActivityIndex\n );\n try {\n await this.indexService.saveDocument({\n collectionName: \"keyvalues\",\n documentData: {\n key: cacheKey,\n value: movedCurrentActivitiesKeys\n }\n });\n } catch(error) {\n console.error(error);\n throw new Error(`Error updating current activities on index`);\n }\n\n console.info(`Moved ${srcActivityType} activity \"${srcActivityKey}\" ` +\n `within activities[${srcActivityIndex} -> ${dstActivityIndex}]\"`);\n this.notificationService.showUINotification({\n message: `notifications:${srcActivityType}Moved`,\n status: \"success\",\n group: \"private\"\n });\n return true;\n}\n","// Define the \"moveActivity\" screen method\n\n// Import method modules\nimport {\n addAnalysisActivityFromTaskActivityToActivities\n} from \"screens/private/activities/add-analysis-activity-from-task-activity-to-activities\";\nimport {\n removeAnalysisActivityFromActivities\n} from \"screens/private/activities/remove-analysis-activity-from-activities\";\nimport {\n removeAnalysisActivityFromTaskActivity\n} from \"screens/private/activities/remove-analysis-activity-from-task-activity\";\nimport {\n moveActivityWithinActivities\n} from \"screens/private/activities/move-activity-within-activities\";\n\n\n// moveActivity: async\nexport async function moveActivity({\n eventName,\n srcGroupId,\n srcGroupIndex,\n dstGroupId,\n dstGroupIndex\n}) {\n this.userService.conditionallyExtendSession(); // do not await\n\n const [ srcGroupName, srcEntityKey ] = srcGroupId.split(\"/\");\n const [dstGroupName] = dstGroupId.split(\"/\");\n switch (eventName) {\n case \"add\": // add analysis activity\n switch (srcGroupName) {\n case \"taskActivity\": // from task activity\n return await addAnalysisActivityFromTaskActivityToActivities\n .call(this, {\n taskAnalysisActivityIndex: srcGroupIndex,\n taskActivityKey: srcEntityKey,\n activitiesIndex: dstGroupIndex\n });\n default: // should never happen\n console.warn(`Unexpected drag and drop source group ` +\n `\"${srcGroupName}\" upon adding analysis activity ` +\n `to activities`);\n return false;\n }\n case \"remove\": // remove analysis activity\n switch (srcGroupName) {\n case \"activities\": // from activities\n return await removeAnalysisActivityFromActivities\n .call(this, {\n analysisActivityIndex: srcGroupIndex\n });\n case \"taskActivity\": // from task activity\n return await removeAnalysisActivityFromTaskActivity\n .call(this, {\n taskAnalysisActivityIndex: srcGroupIndex,\n taskActivityKey: srcEntityKey\n });\n default: // should never happen\n console.warn(`Unexpected drag and drop source group ` +\n `\"${srcGroupName}\" upon removing analysis activity ` +\n `from activities`);\n return false;\n }\n case \"update\": // move activity\n if (srcGroupName === \"activities\" && dstGroupName === \"activities\") {\n return await moveActivityWithinActivities.call(this, {\n srcActivityIndex: srcGroupIndex,\n dstActivityIndex: dstGroupIndex\n });\n }\n return false;\n }\n}\n","// Define the \"removeAnalysisActivityFromActivities\" activities screen\n// utility function\n\n// Import utility modules\nimport { cyrb53 as computeHash } from \"utilities/hash\";\nimport { typeOf } from \"utilities/etc\";\n\n\n// removeAnalysisActivityFromActivities: async\n// returns a promise which resolves with a boolean indicating whether the\n// analysis activity located at the specified index whithin the list of\n// activities has been removed\nexport async function removeAnalysisActivityFromActivities({\n analysisActivityIndex = -1\n}) {\n if (typeOf(analysisActivityIndex) !== \"number\" || \n analysisActivityIndex < 0) {\n throw new Error(`Parameter \"analysisActivityIndex\" should be ` +\n `a non-negative index`);\n } else if(!this.activityService) {\n throw new Error(`Missing required \"activity\" service`);\n } else if(!this.indexService) {\n throw new Error(`Missing required \"index\" service`);\n }\n\n const analysisActivityData =\n this.activitiesData[analysisActivityIndex];\n if (!analysisActivityData) {\n console.warn(`Could not find analysis activity ` +\n `at activities[${analysisActivityIndex}]`);\n return false;\n }\n const { key: analysisActivityKey } = analysisActivityData;\n try {\n await this.activityService.unuseActivity({\n activityKey: analysisActivityKey\n });\n } catch(error) {\n console.warn(`Error removing analysis activity \"${analysisActivityKey}\" ` +\n `from activities[${analysisActivityIndex}] on server`, error);\n return false;\n }\n this.activitiesData.splice(analysisActivityIndex, 1);\n\n // Update array of current activities on index\n const cacheKey = computeHash(`current-activities`);\n const cacheData = await this.indexService.getDocumentByKey({\n collectionName: \"keyvalues\",\n documentKey: cacheKey\n });\n const { value: currentActivitiesKeys = [] } = cacheData || {};\n currentActivitiesKeys.splice(analysisActivityIndex, 1);\n try {\n await this.indexService.saveDocument({\n collectionName: \"keyvalues\",\n documentData: {\n key: cacheKey,\n value: currentActivitiesKeys\n }\n });\n } catch(error) {\n console.warn(`Error removing analysis activity \"${analysisActivityKey}\" ` +\n `from activities[${analysisActivityIndex}] on index`, error);\n return false;\n }\n console.info(`Removed analysis activity \"${analysisActivityKey}\" ` +\n `from activities[${analysisActivityIndex}]`);\n\n return true;\n}\n","// Define the \"removeAnalysisActivityFromTaskActivity\" activities screen\n// utility function\n\n// Import utility modules\nimport { pickProperties } from \"utilities/object\";\nimport { typeOf } from \"utilities/etc\";\n\n// Define internal parameters\nconst updateTaskFields = [ \"title\", \"description\", \"localeCode\" ];\n\n\n// removeAnalysisActivityFromTaskActivity: async\n// returns a promise which resolves with a boolean indicating whether the\n// analysis activity located at the specified index whithin the specified \n// task activity has been removed\nexport async function removeAnalysisActivityFromTaskActivity({\n taskAnalysisActivityIndex = -1,\n taskActivityKey = \"\"\n}) {\n if (typeOf(taskAnalysisActivityIndex) !== \"number\" || \n taskAnalysisActivityIndex < 0) {\n throw new Error(`Parameter \"taskAnalysisActivityIndex\" should be ` +\n `a non-negative index`);\n } else if (typeOf(taskActivityKey) !== \"string\" || !taskActivityKey) {\n throw new Error(`Parameter \"taskActivityKey\" should be a key string`);\n } else if(!this.activityService) {\n throw new Error(`Missing required \"activity\" service`);\n }\n\n const newTaskActivityData = await this.activityService.getActivityItemByKey({\n activityKey: taskActivityKey\n });\n if (!newTaskActivityData) {\n console.error(`Could not find task activity \"${taskActivityKey}\"`);\n return false;\n }\n const { task: newTaskData = null } = newTaskActivityData || {};\n newTaskData.activities.splice(taskAnalysisActivityIndex, 1);\n const newTaskActivitiiesKeys = newTaskData.activities\n .map(({ key: analysisActivityKey = \"\" }) => analysisActivityKey);\n const updateTaskActivityData = {\n taskData: pickProperties(newTaskData, updateTaskFields),\n activityKeys: newTaskActivitiiesKeys\n };\n\n try {\n await this.activityService.updateTaskActivity({ \n taskActivityKey, \n updateTaskActivityData \n });\n } catch(error) {\n console.warn(`Error updating task activity \"${taskActivityKey}\"`, error);\n return false;\n }\n console.info(`Removed analysis activity at index ` +\n `${taskAnalysisActivityIndex} of task \"${taskActivityKey}\"`);\n\n return true;\n}\n","// Define the view-model for the \"activity\" screen\n\n// Import library modules\nimport { inject, computedFrom } from \"aurelia-framework\";\nimport { DialogService } from \"aurelia-dialog\";\nimport { Router } from \"aurelia-router\";\nimport { I18N } from \"aurelia-i18n\";\n\n// Import utility modules\nimport { computeActivityType } from \"utilities/activity\";\nimport {\n setEventHandlers,\n unsetEventHandlers\n} from \"utilities/event\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport { runMode as defaultRunMode } from \"parameters/environment\";\n\n// Import dialog modules\nimport {\n ConfirmCloseUnsavedActivity\n} from \"dialogs/confirm-close-unsaved-activity\";\n\n// Import service modules\nimport ActivityService from \"services/activity\";\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport NotificationService from \"services/notification\";\nimport StateService from \"services/state\";\nimport UserService from \"services/user\";\n\n// Define internal parameters\nconst eventsData = [\n { name: \"analysisActivityDataChanged\", filterBy: \"activityKey\" },\n { name: \"closeActivity\", filterBy: \"activityKey\" },\n { name: \"runModeChanged\" }\n];\n\n\n// Export ActivityScreen class\n@inject(\n ActivityService,\n ApplicationService,\n DialogService,\n EventService,\n I18N,\n NotificationService,\n Router,\n StateService,\n UserService,\n)\nexport class ActivityScreen {\n\n // Local attributes\n activityData = null;\n activitiesIndex = -1; // not among the top-level activities (task activity)\n activityDataModified = false;\n activityDataValid = true;\n taskAnalysisActivityData = null;\n i18nParams = i18nParams;\n runMode = defaultRunMode;\n\n constructor(\n activityService,\n applicationService,\n dialogService,\n eventService,\n i18n,\n notificationService,\n router,\n stateService,\n userService\n ) {\n this.activityService = activityService;\n this.applicationService = applicationService;\n this.dialogService = dialogService;\n this.eventService = eventService;\n this.i18n = i18n;\n this.notificationService = notificationService;\n this.router = router;\n this.stateService = stateService;\n this.userService = userService;\n }\n\n\n // Lifecycle hooks\n // note: route parameter \"activitiesIndex\" has string type and has to be \n // converted to integer!\n async canActivate({ \n activityKey = \"\", \n activitiesIndex = \"\",\n taskAnalysisActivityKey = \"\"\n }) {\n // Load activity data\n try {\n this.activityData = await this.activityService.getActivityItemByKey({\n activityKey\n });\n } catch(error) {\n console.error(`Error getting activity by key \"${activityKey}\"`);\n console.error(error);\n return false;\n }\n if (!this.activityData) {\n console.warn(`Could not get activity by key \"${activityKey}\"`);\n return false;\n }\n if (taskAnalysisActivityKey) {\n try {\n this.taskAnalysisActivityData = \n await this.activityService.getActivityItemByKey({\n activityKey: taskAnalysisActivityKey\n });\n } catch(error) {\n console.error(`Error getting task analysis activity ` +\n `by key \"${taskAnalysisActivityKey}\"`);\n console.error(error);\n return false;\n }\n if (!this.taskAnalysisActivityData) {\n console.warn(`Could not get task analysis activity ` +\n `by key \"${taskAnalysisActivityKey}\"`);\n return false;\n }\n } else {\n this.taskAnalysisActivityData = null;\n }\n\n // Validate and store activitiesIndex\n try {\n activitiesIndex = parseInt(activitiesIndex);\n } catch(error) {\n console.error(`Error parsing integer route parameter \"activitiesIndex\"`);\n return false;\n }\n if (activitiesIndex < -1) {\n console.error(`Parameter \"activitiesIndex\" should be either ` +\n `minus one or a non-negative index`);\n return false;\n }\n this.activitiesIndex = activitiesIndex;\n return true;\n }\n\n async bind() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"activity\",\n entityKey: this.taskAnalysisActivityKey || this.activityKey,\n eventService: this.eventService,\n eventsData\n });\n\n this.runMode = await this.applicationService.getRunMode();\n }\n\n async canDeactivate() {\n if (this.activityDataModified) {\n const confirmCloseActivityDialogOpenResult = await this.dialogService\n .open({\n viewModel: ConfirmCloseUnsavedActivity,\n model: this.taskAnalysisActivityData || this.activityData,\n //keyboard: [\"Escape\"] // keys that close the dialog\n });\n const confirmCloseActivityDialogCloseResult =\n await confirmCloseActivityDialogOpenResult.closeResult;\n if (confirmCloseActivityDialogCloseResult.wasCancelled) {\n console.debug(`User refused to close un-saved activity ` +\n `\"${this.taskAnalysisActivityKey || this.activityKey}\"`);\n return false;\n }\n console.info(`User accepted to close un-saved activity ` +\n `\"${this.taskAnalysisActivityKey || this.activityKey}\"`);\n }\n await this.stateService.purgeStatesByActivity({ \n activityKey: this.activityKey\n });\n return true;\n }\n\n unbind() {\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n deactivate() {\n this.userService.conditionallyExtendSession(); // do not await\n }\n\n\n // Getter and setter methods\n @computedFrom(\"activityData.key\")\n get activityKey() {\n const { key: activityKey = \"\" } = this.activityData || {};\n return activityKey;\n }\n @computedFrom(\"activityData.type\")\n get activityType() {\n return computeActivityType(this.activityData);\n }\n\n @computedFrom(\"taskAnalysisActivityData.key\")\n get taskAnalysisActivityKey() {\n const { \n key: taskAnalysisActivityKey = \"\" \n } = this.taskAnalysisActivityData || {};\n return taskAnalysisActivityKey;\n }\n\n\n // Event filters\n filterEventByActivityKey({ activityKey = \"\" }) {\n return activityKey === this.activityKey ||\n activityKey === this.taskAnalysisActivityKey;\n }\n\n\n // Event handlers\n handleAnalysisActivityDataChangedEvent({\n activityDataModified = false,\n activityDataValid = true\n }) {\n this.activityDataModified = activityDataModified;\n this.activityDataValid = activityDataValid;\n }\n\n handleCloseActivityEvent() {\n try {\n this.router.navigateBack();\n } catch(error) {\n console.warn(`Error navigating to the previous screen`, error);\n return;\n }\n console.info(`Navigating to the previous screen`);\n }\n\n handleRunModeChangedEvent({ runMode = \"dev\" }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the view-model for the \"checkout\" screen\n\n// Import library modules\nimport { Router } from \"aurelia-router\";\nimport { I18N } from \"aurelia-i18n\";\nimport {\n computedFrom,\n inject\n} from \"aurelia-framework\";\n\n// Import utility modules\nimport { setEventHandlers, unsetEventHandlers } from \"utilities/event\";\nimport { capitalizeFirst } from \"utilities/string\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport { \n projectName,\n runMode as defaultRunMode,\n uiLocaleCode,\n spaBaseUrl\n} from \"parameters/environment\";\nimport { \n publishableKey as stripePublishableKey,\n paymentElementAppearance as stripePaymentElementAppearance,\n paymentElementOptions as stripePaymentElementOptions,\n maxIntentDescriptionLength as stripeMaxIntentDescriptionLength,\n maxIntentStatementDescriptorLength as stripeMaxIntentStatementDescriptorLength\n} from \"parameters/stripe\";\n\n// Import method modules\nimport { getOfferByKey } from \"./offer/get-offer-by-key\";\n\n// Import service modules\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport OfferService from \"services/offer\";\nimport StripeService from \"services/stripe\";\nimport UserService from \"services/user\";\n\n// Define internal parameters\nconst knownCurrencyCodes = [\"EUR\", \"GBP\", \"USD\"];\nconst eventsData = [\n { name: \"runModeChanged\" }\n];\n\n\n// Export the \"Checkout\" class\n@inject(\n ApplicationService,\n EventService,\n I18N,\n OfferService,\n Router,\n StripeService,\n UserService\n)\nexport class Checkout {\n\n // Local attributes\n stripe = null;\n stripeElements = null;\n stripeElement = null;\n stripePaymentErrorI18nKey = \"\";\n stripePaymentDeclineMessageI18nKey = \"\";\n stripePaymentDeclineActionI18nKey = \"\";\n stripePaymentIntentData = null;\n offerData = null;\n receiptData = null;\n isProcessingPayment = false;\n i18nParams = i18nParams;\n runMode = defaultRunMode;\n\n constructor(\n applicationService,\n eventService,\n i18n,\n offerService,\n router,\n stripeService,\n userService\n ) {\n this.applicationService = applicationService;\n this.eventService = eventService;\n this.i18n = i18n;\n this.offerService = offerService;\n this.router = router;\n this.stripeService = stripeService;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n async bind() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"offer\",\n eventService: this.eventService,\n eventsData\n });\n }\n\n canActivate({ offerKey = \"\" }) {\n if (!offerKey) {\n console.warn(`Cannot activate checkout screen: ` +\n `missing required parameter \"offerkey\"`);\n return false;\n } else if (!this.userService.isSessionOpen) {\n console.warn(`Cannot activate checkout screen: no open user session`);\n return false;\n }\n return true;\n }\n\n async activate({\n offerKey = \"\", // required\n paymentIntentClientSecret = \"\" // optional\n }) {\n this.offerData = { key: offerKey }; // temporary offer data\n\n // Get run-mode from the index\n this.runMode = await this.applicationService.getRunMode();\n\n // Get offer data\n this.offerData = await getOfferByKey.call(this, {\n offerKey: this.offerKey,\n runMode: this.runMode\n });\n\n // Initialize \"stripe\" object\n const uiLanguageCode = uiLocaleCode.split(\"-\").at(0) || \"en\";\n // eslint-disable-next-line no-undef\n this.stripe = Stripe(stripePublishableKey, {\n locale: uiLanguageCode\n });\n\n // Use existing stripe payment intent (if any)\n let clientSecret;\n if (paymentIntentClientSecret) {\n // Get payment intent from corresponding client secret\n try {\n const { \n paymentIntent: existingPaymentIntentData = null,\n } = await this.stripe.retrievePaymentIntent(paymentIntentClientSecret);\n const { \n client_secret: existingClientSecret = \"\",\n metadata: existingOfferData = null \n } = existingPaymentIntentData || {};\n this.stripePaymentIntentData = existingPaymentIntentData;\n this.offerData = existingOfferData;\n clientSecret = existingClientSecret;\n } catch(error) {\n console.warn(`Error getting payment intent`, error);\n this.stripePaymentIntentData = null;\n this.offerData = null;\n clientSecret = \"\"\n }\n if (clientSecret) {\n console.info(`Existing payment intent \"${this.stripePaymentIntentId}\" ` +\n `for offer \"${this.offerKey}\"`);\n }\n }\n\n // Create stripe payment intent\n if (!clientSecret) {\n const capitalizedProjectName = capitalizeFirst(projectName);\n const i17dOffer = \n capitalizeFirst(this.i18n.tr(\"main:offer\"));\n const i17dOfferName =\n capitalizeFirst(this.i18n.tr(this.offerI18nName));\n const i17dShortOfferName =\n capitalizeFirst(this.i18n.tr(this.offerShortI18nName));\n const i17dOfferSpan = \n capitalizeFirst(this.i18n.tr(this.offerSpanI18nKey));\n const i17dIntentDescriptionItems = [\n `${capitalizedProjectName} App`, \n `${i17dOffer} ${i17dOfferName}`, \n `${i17dOfferSpan} ${this.offerSpanYear}`\n ];\n const i17dIntentDescription = i17dIntentDescriptionItems\n .filter(item => item.length > 0)\n .join(\" - \")\n .slice(0, stripeMaxIntentDescriptionLength);\n const i17dIntentStatementDescriptorItems = [\n `${capitalizedProjectName} App`, \n `${i17dShortOfferName}`, \n ];\n const i17dIntentStatementDescriptor = i17dIntentStatementDescriptorItems\n .filter(item => item.length > 0)\n .join(\"-\")\n .slice(0, stripeMaxIntentStatementDescriptorLength);\n const responseData = await this.stripeService.createPaymentIntent({\n offerKey: this.offerKey,\n intentDescription: i17dIntentDescription,\n intentStatementDescriptor: i17dIntentStatementDescriptor\n });\n const { \n receiptData = null,\n intentData: newPaymentIntentData = null,\n clientSecret: newClientSecret = \"\"\n } = responseData || {};\n this.receiptData = receiptData;\n this.stripePaymentIntentData = newPaymentIntentData;\n clientSecret = newClientSecret;\n }\n if (clientSecret) {\n console.info(`Created payment intent \"${this.stripePaymentIntentId}\" ` +\n `for offer \"${this.offerKey}\"`);\n }\n\n // Instantiate stripe elements object\n this.stripeElements = this.stripe.elements({\n appearance: stripePaymentElementAppearance,\n clientSecret,\n loader: \"always\", // auto|always|never\n locale: uiLanguageCode\n });\n\n // Instantiate stripe payment element object\n this.stripeElement = this.stripeElements\n .create(\"payment\", stripePaymentElementOptions);\n }\n\n attached() {\n if (!this.stripePaymentDomElement) {\n console.warn(\"No stripe payment element\");\n return;\n }\n this.stripeElement.mount(this.stripePaymentDomElement);\n }\n\n unbind() {\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n\n // Getter and setter methods\n @computedFrom(\"offerData.key\")\n get offerKey() {\n const { key: offerKey = \"\" } = this.offerData || {};\n return offerKey;\n }\n\n @computedFrom(\"offerData.name\")\n get offerI18nName() {\n const { name: offerName = \"\" } = this.offerData || {};\n return `enums:longOfferName.${offerName}`;\n }\n @computedFrom(\"offerData.name\")\n get offerShortI18nName() {\n const { name: offerName = \"\" } = this.offerData || {};\n return `enums:shortOfferName.${offerName}`;\n }\n\n @computedFrom(\"offerData.span\")\n get offerSpan() {\n const { span: offerSpan = \"\" } = this.offerData || {};\n return offerSpan;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanI18nKey() {\n return `enums:offerSpan.${this.offerSpan.replace(/\\d+$/, \"\")}`;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanYear() {\n return this.offerSpan.replace(/(\\D+)(\\d+)$/, (_, __, year) => year);\n }\n\n @computedFrom(\"receiptData.finalPrice\")\n get offerFinalPrice() {\n const { finalPrice: offerFinalPrice = 0 } = this.receiptData || {};\n return offerFinalPrice.toFixed(2);\n }\n\n @computedFrom(\"offerData.currencyCode\")\n get offerCurrencyCode() {\n const { currencyCode: offerCurrencyCode = \"\" } = this.offerData || {};\n return offerCurrencyCode;\n }\n @computedFrom(\"offerCurrencyCode\")\n get offerCurrencyCodeI18nKey() {\n return knownCurrencyCodes.includes(this.offerCurrencyCode) ?\n `enums:currencyCodeToSymbol.${this.offerCurrencyCode}` :\n this.offerCurrencyCode;\n }\n\n @computedFrom(\"stripePaymentIntentData.id\")\n get stripePaymentIntentId() {\n const { id: stripePaymentIntentId = \"\" } = this.stripePaymentIntentData || {};\n return stripePaymentIntentId;\n }\n\n\n // Core methods\n async processPayment() {\n console.info(`Processing payment for offer \"${this.offerKey}\"`);\n this.isProcessingPayment = true;\n\n const { \n error: stripePaymentErrorData = null \n } = await this.stripe.confirmPayment({\n elements: this.stripeElements,\n confirmParams: {\n return_url: `${spaBaseUrl}/payment-status/${this.offerKey}`\n }\n });\n\n // This point will only be reached if there is an immediate error when\n // confirming the payment. Otherwise, your customer will be redirected \n // to the \"return_url\". For some payment methods, the customer will be \n // redirected to an intermediate site first to authorize the payment,\n // then redirected to the \"return_url\". \n if (stripePaymentErrorData) {\n const {\n type: stripePaymentErrorType = \"\",\n message: stripePaymentErrorMessage = \"\"\n } = stripePaymentErrorData || {};\n switch (stripePaymentErrorType) {\n case \"card_error\":\n case \"validation_error\": {\n console.warn(`A \"${stripePaymentErrorType}\" error occurred ` +\n `while processing the stripe payment: ${stripePaymentErrorMessage}`);\n this.stripePaymentErrorI18nKey = stripePaymentErrorMessage;\n const rawCardDeclineCode = \n stripePaymentErrorData.decline_code ||\n stripePaymentErrorData.code || \"\";\n let cardDeclineCode;\n // Covering all decline codes except PIN related ones\n switch (rawCardDeclineCode) {\n case \"card_not_supported\":\n case \"not_permitted\":\n cardDeclineCode = \"not_permitted\";\n break;\n case \"approve_with_id\":\n case \"processing_error\":\n case \"reenter_transaction\":\n case \"try_again_later\":\n cardDeclineCode = \"try_again_later\";\n break;\n case \"lost_card\":\n case \"pickup_card\":\n case \"resticted_card\":\n case \"stolen_card\":\n cardDeclineCode = \"stolen_card\"; \n break;\n case \"invalid_account\":\n case \"new_account_information_available\":\n cardDeclineCode = \"invalid_account\"; \n break;\n case \"incorrect_number\":\n case \"invalid_number\":\n cardDeclineCode = \"invalid_number\"; \n break;\n case \"incorrect_cvc\":\n case \"invalid_cvc\":\n cardDeclineCode = \"invalid_cvc\"; \n break;\n case \"call_issuer\":\n case \"do_not_honor\":\n case \"do_not_try_again\":\n case \"generic_decline\":\n case \"merchant_blacklist\":\n case \"no_action_taken\":\n case \"revocation_of_all_authorizations\":\n case \"revocation_of_authorization\":\n case \"security_violation\":\n case \"service_not_allowed\":\n case \"stop_payment_order\":\n case \"transaction_not_allowed\":\n cardDeclineCode = \"generic_decline\"; \n break;\n case \"authentication_required\":\n case \"card_velocity_exceeded\":\n case \"duplicate_transaction\":\n case \"expired_card\":\n case \"fraudulent\":\n case \"insufficient_funds\":\n case \"invalid_expiry_month\":\n case \"invalid_expiry_year\":\n case \"issuer_not_available\":\n case \"testmode_decline\":\n case \"withdrawal_count_limit_exceeded\":\n cardDeclineCode = rawCardDeclineCode;\n break;\n default:\n cardDeclineCode = \"\";\n }\n this.stripePaymentDeclineMessageI18nKey = !cardDeclineCode ? \"\" :\n `errors:payment.stripe.cardDeclineCodes.${cardDeclineCode}.message`;\n this.stripePaymentDeclineActionI18nKey = !cardDeclineCode ? \"\" :\n `errors:payment.stripe.cardDeclineCodes.${cardDeclineCode}.action`;\n break;\n }\n case \"idempotency_error\":\n console.warn(`An \"${stripePaymentErrorType}\" error occurred ` +\n `while processing the stripe payment: ${stripePaymentErrorMessage}`);\n this.stripePaymentErrorI18nKey = \n stripePaymentErrorMessage || \"errors:payment.stripe.idempotency\";\n break;\n case \"api_error\":\n console.warn(`An \"${stripePaymentErrorType}\" error occurred ` +\n `while processing the stripe payment: ${stripePaymentErrorMessage}`);\n this.stripePaymentErrorI18nKey = \n stripePaymentErrorMessage || \"errors:payment.stripe.api\";\n break;\n case \"invalid_request_error\":\n console.warn(`An \"${stripePaymentErrorType}\" error occurred ` +\n `while processing the stripe payment: ${stripePaymentErrorMessage}`);\n this.stripePaymentErrorI18nKey = \n stripePaymentErrorMessage || \"errors:payment.stripe.invalidRequest\";\n break;\n default:\n console.warn(`Unexpected error while processing the stripe payment: ` + \n `${stripePaymentErrorMessage || \"???\"}`);\n this.stripePaymentErrorI18nKey = \n stripePaymentErrorMessage || \"errors:payment.stripe.unexpected\";\n }\n }\n this.isProcessingPayment = false;\n }\n\n async cancelPayment() {\n try {\n await this.stripeService.cancelPaymentIntent({\n paymentIntentId: this.stripePaymentIntentId,\n cancellationReason: \"abandoned\"\n });\n } catch(error) {\n console.warn(`Error canceling payment intent ` +\n `\"${this.stripePaymentIntentId}\" for offer \"${this.offerKey}\"`, error);\n return;\n }\n console.info(`Canceled payment intent \"${this.stripePaymentIntentId}\" ` +\n `for offer \"${this.offerKey}\"`);\n\n // Navigate to the \"offer\" route\n this.router.navigateToRoute(\"offer\", {\n offerKey: this.offerKey\n });\n }\n\n\n // Event handlers\n handleRunModeChangedEvent({ runMode = defaultRunMode }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the view-model for the \"dashboard\" screen\n\n// Import library modules\nimport { computedFrom, inject } from \"aurelia-framework\";\nimport { DialogService } from \"aurelia-dialog\";\nimport { Router } from \"aurelia-router\";\nimport { I18N } from \"aurelia-i18n\";\n\n// Import utility modules\nimport { setEventHandlers, unsetEventHandlers } from \"utilities/event\";\nimport { computeDisplayName } from \"utilities/user\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport { runMode as defaultRunMode } from \"parameters/environment\";\n\n// Import service modules\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport OfferService from \"services/offer\";\nimport UserService from \"services/user\";\n\n// Define internal parameters\nconst eventsData = [\n { name: \"runModeChanged\" }\n];\n\n\n// Export the \"Dashboard\" class\n@inject(\n ApplicationService,\n I18N,\n DialogService,\n EventService,\n OfferService,\n Router,\n UserService\n)\nexport class Dashboard {\n\n // Local attributes\n i18nParams = i18nParams;\n runMode = defaultRunMode;\n\n constructor(\n applicationService,\n i18n,\n dialogService,\n eventService,\n offerService,\n router,\n userService\n ) {\n this.applicationService = applicationService;\n this.i18n = i18n;\n this.dialogService = dialogService;\n this.eventService = eventService;\n this.offerService = offerService;\n this.router = router;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n async activate() {\n this.runMode = await this.applicationService.getRunMode();\n }\n async bind() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"dashboard\",\n eventService: this.eventService,\n eventsData\n });\n }\n\n unbind() {\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n\n // Getter and setter methods\n @computedFrom(\"userService.profile\")\n get userProfileData() {\n const { profileData: userProfileData = null } = this.userService || {};\n return userProfileData;\n }\n @computedFrom(\n \"userProfileData.displayName\", \n \"userProfileData.firstName\", \n \"userProfileData.lastName\"\n )\n get userGreetingName() {\n const { firstName = \"\" } = this.userProfileData || {};\n if (firstName) {\n return firstName;\n }\n const displayName = computeDisplayName({\n profileData: this.userProfileData\n })\n return displayName || this.i18n.tr(\"main:unknown\");\n }\n\n\n\n // Event handlers\n handleRunModeChangedEvent({ runMode = defaultRunMode }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the view-model for the \"invite-user\" screen\n\n// Import library modules\nimport { inject } from \"aurelia-framework\";\nimport QRious from \"qrious\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport { spaBaseUrl } from \"parameters/environment\";\n\n// Import service modules\nimport NotificationService from \"services/notification\";\nimport UserService from \"services/user\";\n\n\n// Export the \"InviteUser\" class\n@inject(\n NotificationService, \n UserService\n)\nexport class InviteUser {\n\n // Local attributes\n inviteUserSignUpUrl = \"\";\n i18nParams = i18nParams;\n\n constructor(notificationService, userService) {\n this.notificationService = notificationService;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n attached() {\n const { userKey } = this.userService;\n const { displayName: userDisplayName } = this.userService.profileData;\n if (userKey && userDisplayName) {\n this.inviteUserSignUpUrl = \n `${spaBaseUrl}/sign-up/${userKey}/${userDisplayName}`;\n if (this.invitingUserQRCodeCanvasElement) {\n this.invitingUserQRCode = new QRious({\n element: this.invitingUserQRCodeCanvasElement,\n value: this.inviteUserSignUpUrl,\n size: 256,\n level: \"H\" // error correction level: L|M|Q|H\n });\n }\n }\n }\n\n\n // Core methods\n copyInvitationLinkToClipboard() {\n navigator.clipboard.writeText(this.inviteUserSignUpUrl);\n this.notificationService.showUINotification({\n message: \"notifications:invitationLinkCopied\",\n status: \"success\",\n group: \"private\"\n });\n }\n\n}\n","// Define the view-model for the \"offer\" screen\n\n// Import library modules\nimport { Router } from \"aurelia-router\";\nimport { I18N } from \"aurelia-i18n\";\nimport {\n computedFrom,\n inject\n} from \"aurelia-framework\";\n\n// Import utility modules\nimport { setEventHandlers, unsetEventHandlers } from \"utilities/event\";\nimport { computeAbsoluteTimeString } from \"utilities/time\";\nimport { roundToDecimal } from \"utilities/number\";\nimport { pause } from \"utilities/promise\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport { runMode as defaultRunMode } from \"parameters/environment\";\nimport { mockOperationDurationMs } from \"parameters/time\";\n\n// Import method modules\nimport { getOfferByKey } from \"./offer/get-offer-by-key\";\nimport { getOfferDiscountsData } from \"./offer/get-offer-discounts-data\";\n\n// Import service modules\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport OfferService from \"services/offer\";\nimport UserService from \"services/user\";\n\n// Define internal parameters\nconst knownCurrencyCodes = [\"EUR\", \"GBP\", \"USD\"];\nconst eventsData = [\n { name: \"runModeChanged\" }\n];\n\n\n// Export the \"Offer\" class\n@inject(\n ApplicationService,\n EventService,\n I18N,\n OfferService,\n Router,\n UserService\n)\nexport class Offer {\n\n // Local attributes\n offerData = null;\n offerDiscountsData = null;\n offerActivationData = undefined;\n offerActivationStatus = \"\";\n i18nParams = i18nParams;\n runMode = defaultRunMode;\n\n constructor(\n applicationService,\n eventService,\n i18n,\n offerService,\n router,\n userService\n ) {\n this.applicationService = applicationService;\n this.eventService = eventService;\n this.i18n = i18n;\n this.offerService = offerService;\n this.router = router;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n async bind() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"offer\",\n eventService: this.eventService,\n eventsData\n });\n }\n\n canActivate({ offerKey = \"\" }) {\n return Boolean(offerKey) && this.userService.isSessionOpen;\n }\n\n async activate({ offerKey = \"\" }) {\n this.offerData = { key: offerKey }; // temporary offer data\n\n // Get run-mode from the index\n this.runMode = await this.applicationService.getRunMode();\n\n // Get offer data\n this.offerData = await getOfferByKey.call(this, {\n offerKey: this.offerKey,\n runMode: this.runMode\n });\n\n // Get discounts data\n this.offerDiscountsData = \n await getOfferDiscountsData.call(this, this.offerKey);\n }\n\n unbind() {\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n\n // Getter and setter methods\n @computedFrom(\"offerData.key\")\n get offerKey() {\n const { key: offerKey = \"\" } = this.offerData || {};\n return offerKey;\n }\n\n @computedFrom(\"offerData.name\")\n get offerI18nName() {\n const { name: offerName = \"\" } = this.offerData || {};\n return `enums:longOfferName.${offerName}`;\n }\n\n @computedFrom(\"offerData.type\")\n get offerType() {\n const { type: offerType = \"\" } = this.offerData || {};\n return offerType;\n }\n\n @computedFrom(\"offerData.target\")\n get offerTarget() {\n const { target: offerTarget = \"usr\" } = this.offerData || {};\n return offerTarget;\n }\n @computedFrom(\"offerTarget\")\n get isTeachersOffer() {\n return this.offerTarget === \"tch\";\n }\n\n @computedFrom(\"offerData.span\")\n get offerSpan() {\n const { span: offerSpan = \"\" } = this.offerData || {};\n return offerSpan;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanI18nKey() {\n return `enums:offerSpan.${this.offerSpan.replace(/\\d+$/, \"\")}`;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanYear() {\n return this.offerSpan.replace(/(\\D+)(\\d+)$/, (_, __, year) => year);\n }\n\n @computedFrom(\"offerData.availabilityWindow\")\n get offerExpiryMs() {\n const { \n availabilityWindow: offerAvailabilityWindow = []\n } = this.offerData || {};\n return offerAvailabilityWindow.at(1) || null;\n }\n @computedFrom(\"offerExpiryMs\")\n get offerExpiryDateI18nString() {\n return this.offerExpiryMs ? computeAbsoluteTimeString({\n timeStampMs: this.offerExpiryMs,\n formatString: \"P\", // Long localized date (e.g. 05/31/2020|31/05/2020)\n localeCode: this.i18n.getLocale()\n }) : \"\";\n }\n\n @computedFrom(\"offerData.originalPrice\")\n get offerOriginalPrice () {\n const { originalPrice: offerOriginalPrice = 0 } = this.offerData || {};\n return roundToDecimal(offerOriginalPrice, 2);\n }\n @computedFrom(\"offerData.discountedPrice\")\n get offerDiscountedPrice () {\n const { discountedPrice: offerDiscountedPrice = 0 } = this.offerData || {};\n return roundToDecimal(offerDiscountedPrice, 2);\n }\n @computedFrom(\"offerData.discountRate\")\n get offerDiscountRate() {\n const { discountRate: offerDiscountRate = 0 } = this.offerData || {};\n return offerDiscountRate;\n }\n @computedFrom(\"offerDiscountRate\")\n get offerDiscountRatePercent() {\n return roundToDecimal(100*this.offerDiscountRate, 0);\n }\n\n @computedFrom(\"offerData.vatRate\")\n get offerVatRate() {\n const { vatRate: offerVatRate = \"\" } = this.offerData || {};\n return offerVatRate;\n }\n @computedFrom(\"offerVatRate\")\n get offerVatRatePercent() {\n return roundToDecimal(100*this.offerVatRate, 0);\n }\n\n @computedFrom(\"offerData.currencyCode\")\n get offerCurrencyCode() {\n const { currencyCode: offerCurrencyCode = \"\" } = this.offerData || {};\n return offerCurrencyCode;\n }\n @computedFrom(\"offerCurrencyCode\")\n get offerCurrencyCodeI18nKey() {\n return knownCurrencyCodes.includes(this.offerCurrencyCode) ?\n `enums:currencyCodeToSymbol.${this.offerCurrencyCode}` :\n this.offerCurrencyCode;\n }\n @computedFrom(\"offerCurrencyCode\")\n get grossPriceThreshold() {\n switch (this.offerCurrencyCode) {\n case \"EUR\": // Euro \n case \"USD\": // U.S. Dollar \n case \"GBP\": // Great Britain Pound\n case \"CHF\": // Swiss Franc \n return 0.5;\n default:\n if (this.offerCurrencyCode) {\n console.warn(`Using default gross price threshold of 1.0 ` +\n `${this.offerCurrencyCode}: please verify that this makes sense`);\n }\n return 1.0;\n }\n }\n\n @computedFrom(\"offerData.duration\")\n get offerDurationString() {\n const { duration: offerDurationString = \"\" } = this.offerData || {};\n return offerDurationString;\n }\n @computedFrom(\"offerDurationString\")\n get offerDurationMonths() {\n const [offerDurationMonthsString = \"\"] =\n this.offerDurationString.match(/^\\d+/);\n return offerDurationMonthsString ? Number(offerDurationMonthsString) : 0;\n }\n @computedFrom(\"offerData.credit\")\n get offerCredit() {\n const { credit: offerCredit = 0 } = this.offerData || {};\n return offerCredit;\n }\n\n @computedFrom(\"offerType\", \"offerTarget\")\n get offerPointsData() {\n switch (this.offerType) {\n case \"flt\": // flat offer\n switch (this.offerTarget) {\n case \"usr\":\n return [{\n i18nKey: \"elements:offer.useWithoutLimits\",\n i18nParams: {\n ...i18nParams,\n durationMonths: this.offerDurationMonths\n }\n }, {\n i18nKey: \"elements:offer.freezeYourCredit\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.recommendedForHeavyUsers\",\n i18nParams: i18nParams\n }];\n case \"tch\":\n return [{\n i18nKey: \"elements:offer.freeOfferReservedForTeachers\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.useWithoutLimits\",\n i18nParams: {\n ...i18nParams,\n durationMonths: this.offerDurationMonths\n }\n }, {\n i18nKey: \"elements:offer.freezeYourCredit\",\n i18nParams\n }];\n default:\n throw new Error(`Unknown flat offer target \"${this.offerTarget}\"`);\n }\n case \"crd\": // credit offer\n switch (this.offerTarget) {\n case \"usr\":\n return [{\n i18nKey: \"elements:offer.topUpYourCredit\",\n i18nParams: {\n ...i18nParams,\n credit: this.offerCredit\n }\n }, {\n i18nKey: \"elements:offer.recommendedForCasualUsers\",\n i18nParams: i18nParams\n }];\n case \"tch\":\n return [{\n i18nKey: \"elements:offer.freeOfferReservedForTeachers\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.topUpYourCredit\",\n i18nParams: {\n ...i18nParams,\n credit: this.offerCredit\n }\n }, {\n i18nKey: \"elements:offer.recommendedForCasualUsers\",\n i18nParams: i18nParams\n }];\n case \"dnr\":\n return [{\n i18nKey: \"elements:offer.offerAimedAtDonors\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.supportOurWork\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.obtainOneSymbolicCredit\",\n i18nParams\n }, {\n i18nKey: \"elements:offer.earnOurLoveAndGratitude\",\n i18nParams\n }];\n default:\n throw new Error(`Unknown credit offer target \"${this.offerTarget}\"`);\n }\n default:\n console.warn(`Unknown offer type \"${this.offerType}\"`);\n return [];\n }\n }\n\n @computedFrom(\"offerOriginalPrice\", \"offerDiscountedPrice\")\n get offerBaseDiscountAmount () {\n return this.offerOriginalPrice - this.offerDiscountedPrice;\n }\n\n @computedFrom(\"offerDiscountsData.inviteeDiscountRate\")\n get offerInviteeDiscountRate() {\n const { \n inviteeDiscountRate: offerInviteeDiscountRate = 0 \n } = this.offerDiscountsData || {};\n return offerInviteeDiscountRate;\n }\n @computedFrom(\"offerInviteeDiscountRate\")\n get offerInviteeDiscountRatePercent() {\n return roundToDecimal(100*this.offerInviteeDiscountRate, 0);\n }\n @computedFrom(\"offerOriginalPrice\", \"offerInviteeDiscountRate\")\n get offerInviteeDiscountAmount() {\n return roundToDecimal(\n this.offerInviteeDiscountRate*this.offerOriginalPrice, 2\n );\n }\n\n @computedFrom(\"offerDiscountsData.invitedFlatOffersUsersCount\")\n get offerInvitedFlatOffersUsersCount() {\n const { \n invitedFlatOffersUsersCount: offerInvitedFlatOffersUsersCount = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedFlatOffersUsersCount;\n }\n @computedFrom(\"offerDiscountsData.invitedFlatOffersMonthsCount\")\n get offerInvitedFlatOffersMonthsCount() {\n const { \n invitedFlatOffersMonthsCount: offerInvitedFlatOffersMonthsCount = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedFlatOffersMonthsCount;\n }\n @computedFrom(\"offerDiscountsData.invitedFlatOffersDiscountRate\")\n get offerInvitedFlatOffersDiscountRate() {\n const { \n invitedFlatOffersDiscountRate: offerInvitedFlatOffersDiscountRate = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedFlatOffersDiscountRate;\n }\n @computedFrom(\"offerInvitedFlatOffersDiscountRate\")\n get offerInvitedFlatOffersDiscountRatePercent() {\n return roundToDecimal(100*this.offerInvitedFlatOffersDiscountRate, 0);\n }\n @computedFrom(\"offerOriginalPrice\", \"offerInvitedFlatOffersDiscountRate\")\n get offerInvitedFlatOffersDiscountAmount() {\n return roundToDecimal(\n this.offerInvitedFlatOffersDiscountRate*this.offerOriginalPrice, 2\n );\n }\n\n @computedFrom(\"offerDiscountsData.invitedCreditOffersUsersCount\")\n get offerInvitedCreditOffersUsersCount() {\n const { \n invitedCreditOffersUsersCount: offerInvitedCreditOffersUsersCount = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedCreditOffersUsersCount;\n }\n @computedFrom(\"offerDiscountsData.invitedCreditOffersCredits\")\n get offerInvitedCreditOffersCreditsCount() {\n const { \n invitedCreditOffersCreditsCount: offerInvitedCreditOffersCreditsCount = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedCreditOffersCreditsCount;\n }\n @computedFrom(\"offerDiscountsData.invitedCreditOffersDiscountRate\")\n get offerInvitedCreditOffersDiscountRate() {\n const { \n invitedCreditOffersDiscountRate: offerInvitedCreditOffersDiscountRate = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedCreditOffersDiscountRate;\n }\n @computedFrom(\"offerInvitedCreditOffersDiscountRate\")\n get offerInvitedCreditOffersDiscountRatePercent() {\n return roundToDecimal(100*this.offerInvitedCreditOffersDiscountRate, 0);\n }\n @computedFrom(\"offerOriginalPrice\", \"offerInvitedCreditOffersDiscountRate\")\n get offerInvitedCreditOffersDiscountAmount() {\n return roundToDecimal(\n this.offerInvitedCreditOffersDiscountRate*this.offerOriginalPrice, 2\n );\n }\n\n @computedFrom(\"offerDiscountsData.invitedNoOfferUsersCount\")\n get offerInvitedNoOfferUsersCount() {\n const { \n invitedNoOfferUsersCount: offerInvitedNoOfferUsersCount = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedNoOfferUsersCount;\n }\n @computedFrom(\"offerDiscountsData.invitedNoOfferDiscountRate\")\n get offerInvitedNoOfferDiscountRate() {\n const { \n invitedNoOfferDiscountRate: offerInvitedNoOfferDiscountRate = 0 \n } = this.offerDiscountsData || {};\n return offerInvitedNoOfferDiscountRate;\n }\n @computedFrom(\"offerInvitedNoOfferDiscountRate\")\n get offerInvitedNoOfferDiscountRatePercent() {\n return roundToDecimal(100*this.offerInvitedNoOfferDiscountRate, 0);\n }\n @computedFrom(\"offerOriginalPrice\", \"offerInvitedNoOfferDiscountRate\")\n get offerInvitedNoOfferDiscountAmount() {\n return roundToDecimal(\n this.offerInvitedNoOfferDiscountRate*this.offerOriginalPrice, 2\n );\n }\n\n @computedFrom(\n \"offerOriginalPrice\",\n \"offerBaseDiscountAmount\",\n \"offerInviteeDiscountAmount\",\n \"offerInvitedFlatOffersDiscountAmount\",\n \"offerInvitedCreditOffersDiscountAmount\",\n \"offerInvitedNoOfferDiscountAmount\"\n )\n get offerThresholdDiscountAmount() {\n const grossPrice = this.offerOriginalPrice - (\n this.offerBaseDiscountAmount +\n this.offerInviteeDiscountAmount + \n this.offerInvitedFlatOffersDiscountAmount +\n this.offerInvitedCreditOffersDiscountAmount +\n this.offerInvitedNoOfferDiscountAmount\n );\n return grossPrice < this.grossPriceThreshold ? grossPrice : 0;\n } \n\n @computedFrom(\n \"offerInviteeDiscountAmount\",\n \"offerInvitedFlatOffersDiscountAmount\",\n \"offerInvitedCreditOffersDiscountAmount\",\n \"offerInvitedNoOfferDiscountAmount\",\n \"offerThresholdDiscountAmount\"\n )\n get offerAdditionalDiscountAmount() {\n return this.offerInviteeDiscountAmount + \n this.offerInvitedFlatOffersDiscountAmount +\n this.offerInvitedCreditOffersDiscountAmount +\n this.offerInvitedNoOfferDiscountAmount +\n this.offerThresholdDiscountAmount;\n } \n\n @computedFrom(\n \"offerOriginalPrice\", \n \"offerBaseDiscountAmount\",\n \"offerAdditionalDiscountAmount\"\n ) \n get offerGrossPrice() {\n return roundToDecimal(Math.max(this.offerOriginalPrice - (\n this.offerBaseDiscountAmount +\n this.offerAdditionalDiscountAmount\n )), 2);\n }\n\n @computedFrom(\"offerGrossPrice\", \"offerVatRate\")\n get offerVatAmount() {\n return roundToDecimal(this.offerGrossPrice * this.offerVatRate, 2);\n }\n\n @computedFrom(\"offerGrossPrice\", \"offerVatAmount\")\n get offerFinalPrice() {\n return roundToDecimal(this.offerGrossPrice + this.offerVatAmount, 2);\n }\n\n @computedFrom(\"offerDiscountsData\")\n get offerInvoiceItemsData() { \n return [{\n show: true,\n i18nKey: \"main:originalPrice\",\n i18nParams,\n valueString: `${this.offerOriginalPrice.toFixed(2)}`\n }, {\n show: this.offerBaseDiscountAmount > 0,\n i18nKey: \"main:baseDiscountAmount\",\n i18nParams: {\n ...i18nParams,\n discountRatePercent: this.offerDiscountRatePercent\n },\n valueString: `- ${this.offerBaseDiscountAmount.toFixed(2)}`\n }, {\n show: this.offerInviteeDiscountRate > 0,\n i18nKey: \"main:inviteeDiscount\",\n i18nParams: {\n ...i18nParams,\n discountRatePercent: this.offerInviteeDiscountRatePercent\n },\n valueString: `- ${this.offerInviteeDiscountAmount.toFixed(2)}`\n }, {\n show: this.offerInvitedFlatOffersDiscountRate > 0,\n i18nKey: \"main:invitedFlatOffersDiscount\",\n i18nParams: {\n ...i18nParams,\n usersCount: this.offerInvitedFlatOffersUsersCount,\n monthsCount: this.offerInvitedFlatOffersMonthsCount,\n discountRatePercent: this.offerInvitedFlatOffersDiscountRatePercent\n },\n valueString: `- ${this.offerInvitedFlatOffersDiscountAmount.toFixed(2)}`\n }, {\n show: this.offerInvitedCreditOffersDiscountRate > 0,\n i18nKey: \"main:invitedCreditOffersDiscount\",\n i18nParams: {\n ...i18nParams,\n usersCount: this.offerInvitedCreditOffersUsersCount,\n creditsCount: this.offerInvitedCreditOffersCreditsCount,\n discountRatePercent: this.offerInvitedCreditOffersDiscountRatePercent\n },\n valueString: `- ${this.offerInvitedCreditOffersDiscountAmount.toFixed(2)}`\n }, {\n show: this.offerInvitedNoOfferDiscountRate > 0,\n i18nKey: \"main:invitedNoOfferDiscount\",\n i18nParams: {\n ...i18nParams,\n usersCount: this.offerInvitedNoOfferUsersCount,\n discountRatePercent: this.offerInvitedNoOfferDiscountRatePercent\n },\n valueString: `- ${this.offerInvitedNoOfferDiscountAmount.toFixed(2)}`\n }, {\n show: this.offerThresholdDiscountAmount > 0,\n i18nKey: \"main:thresholdDiscount\",\n i18nParams,\n valueString: `- ${this.offerThresholdDiscountAmount.toFixed(2)}`\n }, {\n show: true,\n i18nKey: \"main:grossPrice\",\n i18nParams,\n valueString: `${this.offerGrossPrice.toFixed(2)}`\n }, {\n show: true,\n i18nKey: \"main:vatAmount\",\n i18nParams: {\n ...i18nParams,\n vatRatePercent: this.offerVatRatePercent\n },\n valueString: `+ ${this.offerVatAmount.toFixed(2)}`\n }, {\n show: true,\n i18nKey: \"main:finalPrice\",\n i18nParams,\n valueString: `${this.offerFinalPrice.toFixed(2)}`\n }].map(offerInvoiceItemData => {\n const { i18nKey = \"\" } = offerInvoiceItemData || {};\n return {\n ...offerInvoiceItemData,\n showThinSeparationLine: /grossPrice$/.test(i18nKey),\n showThickSeparationLine: /(final)Price$/.test(i18nKey),\n showAsterisk: /(Credit|Flat|No)Offers?Discount$/.test(i18nKey)\n };\n });\n }\n\n @computedFrom(\"offerDiscountsData.invitationWindowStartsOn\")\n get offerDiscountWindowStartsOn() {\n const {\n invitationWindowStartsOn: offerDiscountWindowStartsOn = 0\n } = this.offerDiscountsData || {};\n return offerDiscountWindowStartsOn;\n }\n @computedFrom(\"offerDiscountsData.invitationWindowStartsWithOfferActivation\")\n get discountWindowStartsWithOfferActivation() {\n const {\n invitationWindowStartsWithOfferActivation: \n discountWindowStartsWithOfferActivation = false\n } = this.offerDiscountsData || {};\n return discountWindowStartsWithOfferActivation;\n }\n @computedFrom(\"offerDiscountWindowStartsOn\")\n get offerDiscountWindowStartsOnI18nParams() {\n const startDate = computeAbsoluteTimeString({\n timeStampMs: this.offerDiscountWindowStartsOn,\n formatString: \"P\", // Long localized date (e.g. 05/31/2020|31/05/2020)\n localeCode: this.i18n.getLocale()\n });\n return { ...i18nParams, startDate };\n }\n @computedFrom(\n \"offerInvitedFlatOffersDiscountRate\",\n \"offerInvitedCreditOffersDiscountRate\",\n \"offerInvitedNoOfferDiscountRate\"\n )\n get showDiscountsComputedStartingOn() {\n return this.offerInvitedFlatOffersDiscountRate > 0 ||\n this.offerInvitedCreditOffersDiscountRate > 0 ||\n this.offerInvitedNoOfferDiscountRate > 0;\n }\n\n @computedFrom(\"offerActivationData.activatesData\")\n get activatesEdgeData() {\n const {\n activatesData: activatesEdgeData = null\n } = this.offerActivationData || null;\n return activatesEdgeData;\n }\n @computedFrom(\"activatesEdgeData.activatedOn\")\n get offerActivationDate() {\n const { activatedOn: offerActivatedOnMs = 0 } = this.activatesEdgeData || {};\n return computeAbsoluteTimeString({\n timeStampMs: offerActivatedOnMs,\n formatString: \"P\",\n localeCode: this.uiLocaleCode\n });\n }\n @computedFrom(\"activatesEdgeData.expiresOn\")\n get offerExpiryDate() {\n const { expiresOn: offerExpiresOnMs = 0 } = this.activatesEdgeData || {};\n return computeAbsoluteTimeString({\n timeStampMs: offerExpiresOnMs,\n formatString: \"P\",\n localeCode: this.uiLocaleCode\n });\n }\n\n @computedFrom(\"offerActivationStatus\")\n get offerActivationAlertClass() {\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n console.info(`Offer is being activated…`);\n return `uk-alert`;\n case \"succeeded\":\n console.info(`Offer activation succeeded!`);\n return `uk-alert uk-alert-success`;\n case \"failed\":\n console.warn(`Offer activation failed.`);\n return `uk-alert uk-alert-danger`;\n }\n }\n @computedFrom(\"offerActivationStatus\")\n get offerActivationMainMessageI18nKey() {\n const status = this.offerActivationStatus;\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n case \"succeeded\":\n case \"failed\":\n return `screens:offer.activationStatuses.${status}.mainMessage`;\n }\n }\n @computedFrom(\"offerActivationStatus\", \"offerType\")\n get offerActivationActionMessageI18nKey() {\n const status = this.offerActivationStatus;\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n case \"failed\":\n return `screens:offer.activationStatuses.${status}.actionMessage`;\n case \"succeeded\": {\n const actionSubMessage = this.offerType === \"crd\" ? \"creditOffer\" :\n this.offerType === \"flt\" ? \"flatOffer\" : \"\";\n return `screens:paymentStatus.activationStatuses.${status}` +\n `.actionMessage.${actionSubMessage}`;\n }\n }\n }\n @computedFrom(\"offerActivationStatus\", \"offerType\")\n get offerActivationActionMessageI18nParams() {\n switch (this.offerActivationStatus) {\n case \"\":\n case \"processing\":\n case \"failed\":\n return i18nParams;\n case \"succeeded\": {\n switch (this.offerType) {\n case \"crd\": // credit offer\n return {\n ...i18nParams,\n offerCredit: this.offerCredit\n };\n case \"flt\": // flat offer\n return {\n ...i18nParams,\n offerActivationDate: this.offerActivationDate,\n offerExpiryDate: this.offerExpiryDate,\n offerDurationMonths: this.offerDurationMonths\n };\n default:\n return i18nParams;\n }\n }\n }\n }\n\n\n // Core methods\n async activateOfferForFree() {\n this.offerActivationStatus = \"processing\";\n if ([\"dev\"].includes(this.runMode)) {\n console.warn(`Mocking offer activation duration ` +\n `while in \"${this.runMode}\" mode`);\n await pause(mockOperationDurationMs);\n }\n this.offerActivationData = await this.offerService.activateOfferForFree({ \n offerKey: this.offerKey \n });\n if (this.offerActivationData) {\n this.offerActivationStatus = \"succeeded\";\n console.info(`Activated offer \"${this.offerKey}\" for free`);\n } else {\n this.offerActivationStatus = \"failed\";\n console.warn(`Could not activate offer \"${this.offerKey}\" for free`);\n }\n }\n\n async purchaseOffer() {\n console.info(`Purchasing offer \"${this.offerKey}\" ` +\n `for ${this.offerFinalPrice} ${this.offerCurrencyCode}`);\n this.router.navigateToRoute(`checkout`, {\n offerKey: this.offerKey\n });\n }\n\n\n // Event handlers\n handleRunModeChangedEvent({ runMode = defaultRunMode }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the \"getOfferByKey\" offer screen method\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\nimport { pause } from \"utilities/promise\";\n\n// Import parameter modules\nimport { runMode as defaultRunMode } from \"parameters/environment\";\nimport { mockLoadingDataDurationMs } from \"parameters/time\";\n\n\n// getOfferByKey: async \n// returns a promise which resolves either with an offer object\n// corresponding to the specified key, or with null if an error occurred\nexport async function getOfferByKey({\n offerKey = \"\",\n runMode = defaultRunMode\n}) {\n if (typeOf(offerKey) !== \"string\" || !offerKey) {\n throw new Error(`Parameter \"offerKey\" should be a non-empty string`);\n } else if (typeOf(runMode) !== \"string\") {\n throw new Error(`Parameter \"runMode\" should be a string`);\n } else if (!this.offerService) {\n throw new Error(`Missing required \"offer\" service`);\n }\n\n this.isLoadingData = true;\n\n // Mock finite loading duration\n if ([\"dev\"].includes(runMode)) {\n await pause(mockLoadingDataDurationMs);\n }\n\n // Get offer by key from index/server\n let offerData;\n try {\n offerData = await this.offerService.getOfferByKey(offerKey);\n } catch(error) {\n console.warn(`Error fetching offer \"${offerKey} from index/server`, error);\n offerData = null;\n }\n this.isLoadingData = false;\n\n // Return offer data object\n return offerData;\n}\n","// Define the \"getOfferDiscountsData\" offer screen method\n\n// Import utility modules\nimport { typeOf } from \"utilities/etc\";\n\n\n// getOfferDiscountsData: async\n// returns a promise which either resolves with an object containing\n// discounts data to be applied to the specified offer upon activation,\n// or rejects with an error\nexport async function getOfferDiscountsData(offerKey) {\n if (typeOf(offerKey) !== \"string\" || !offerKey) {\n throw new Error(`Parameter \"offerKey\" should be a non-empty string`);\n } else if (!this.offerService) {\n throw new Error(`Missing required \"offer\" service`);\n }\n\n this.isLoadingData = true;\n let offerAdditionalDiscountsData;\n try {\n offerAdditionalDiscountsData = \n await this.offerService.getOfferAdditionalDiscounts(offerKey);\n } catch(error) {\n console.warn(`Error getting additional discounts data for offer ` +\n `\"${this.offerKey}\"`, error);\n offerAdditionalDiscountsData = null;\n }\n this.isLoadingData = false;\n\n // Return offer's additional discounts data\n return offerAdditionalDiscountsData;\n}\n","// Define the view-model for the \"payment-status\" screen\n\n// Import library modules\nimport { Router } from \"aurelia-router\";\nimport { I18N } from \"aurelia-i18n\";\nimport ms from \"ms\";\nimport {\n computedFrom,\n inject\n} from \"aurelia-framework\";\n\n// Import utility modules\nimport { setEventHandlers, unsetEventHandlers } from \"utilities/event\";\nimport { computeAbsoluteTimeString } from \"utilities/time\";\nimport { pause } from \"utilities/promise\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\nimport {\n runMode as defaultRunMode,\n uiLocaleCode as defaultUILocaleCode\n} from \"parameters/environment\";\nimport { \n publishableKey as stripePublishableKey\n} from \"parameters/stripe\";\n\n// Import method modules\nimport { getOfferByKey } from \"./offer/get-offer-by-key\";\n\n// Import service modules\nimport ApplicationService from \"services/application\";\nimport EventService from \"services/event\";\nimport OfferService from \"services/offer\";\nimport StripeService from \"services/stripe\";\nimport UserService from \"services/user\";\n\n// Define internal parameters\nconst maxElapsedTimeMs = ms(\"30sec\");\nconst eventsData = [\n { name: \"runModeChanged\" }\n];\n\n\n// Export the \"PaymentStatus\" class\n@inject(\n ApplicationService,\n EventService,\n I18N,\n OfferService,\n Router,\n StripeService,\n UserService\n)\nexport class PaymentStatus {\n\n // Local attributes\n stripe = null;\n offerData = null;\n paymentIntentClientSecret = \"\";\n paymentIntentData = null;\n paymentIntentStatus = \"processing\";\n offerActivationData = null;\n offerActivationStatus = \"processing\";\n offerActivationMainMessageI18nParams = null;\n uiLocaleCode = defaultUILocaleCode;\n i18nParams = i18nParams;\n runMode = defaultRunMode;\n\n constructor(\n applicationService,\n eventService,\n i18n,\n offerService,\n router,\n stripeService,\n userService\n ) {\n this.applicationService = applicationService;\n this.eventService = eventService;\n this.i18n = i18n;\n this.offerService = offerService;\n this.router = router;\n this.stripeService = stripeService;\n this.userService = userService;\n }\n\n\n // Lifecycle methods\n canActivate({ offerKey = \"\" }) {\n if (!offerKey) {\n console.warn(`Cannot activate payment-status screen: ` +\n `missing required parameter \"offerkey\"`);\n return false;\n } else if (!this.userService.isSessionOpen) {\n console.warn(`Cannot activate checkout screen: no open user session`);\n return false;\n }\n\n // Get payment intent client secret from url search parameters\n const urlSearchParams = new URLSearchParams(window.location.search);\n const paymentIntentClientSecret = urlSearchParams\n .get(\"payment_intent_client_secret\");\n if (!paymentIntentClientSecret) {\n console.warn(`Cannot activate checkout screen: ` +\n `missing payment intent's client secret in query string`);\n return false;\n }\n this.paymentIntentClientSecret = paymentIntentClientSecret;\n return true;\n }\n\n async activate({ offerKey = \"\" }) {\n this.offerData = { key: offerKey }; // temporary offer data\n\n // Get run-mode from the index\n this.runMode = await this.applicationService.getRunMode();\n\n // Get current uiLocaleCode\n this.uiLocaleCode = this.i18n.getLocale();\n\n // Get offer data\n this.offerData = await getOfferByKey.call(this, {\n offerKey: this.offerKey,\n runMode: this.runMode\n });\n\n // Initialize \"stripe\" object\n const uiLanguageCode = this.uiLocaleCode.split(\"-\").at(0) || \"en\";\n this.stripe = Stripe(stripePublishableKey, { // eslint-disable-line no-undef\n locale: uiLanguageCode\n });\n }\n\n async bind() {\n this.eventsSubscriptions = setEventHandlers.call(this, {\n entityName: \"offer\",\n eventService: this.eventService,\n eventsData\n });\n }\n\n async attached() {\n // Update payment intent data\n await this.updatePaymentIntentStatus();\n }\n\n unbind() {\n unsetEventHandlers.call(this, {\n eventsSubscriptions: this.eventsSubscriptions\n });\n }\n\n\n // Getter and setter methods\n @computedFrom(\"offerData.key\")\n get offerKey() {\n const { key: offerKey = \"\" } = this.offerData || {};\n return offerKey;\n }\n @computedFrom(\"offerData.type\")\n get offerType() {\n const { type: offerType = \"\" } = this.offerData || {};\n return offerType;\n }\n @computedFrom(\"offerData.target\")\n get offerTarget() {\n const { target: offerTarget = \"usr\" } = this.offerData || {};\n return offerTarget;\n }\n @computedFrom(\"offerData.name\")\n get offerI18nName() {\n const { name: offerName = \"\" } = this.offerData || {};\n return `enums:longOfferName.${offerName}`;\n }\n @computedFrom(\"offerData.span\")\n get offerSpan() {\n const { span: offerSpan = \"\" } = this.offerData || {};\n return offerSpan;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanI18nKey() {\n return `enums:offerSpan.${this.offerSpan.replace(/\\d+$/, \"\")}`;\n }\n @computedFrom(\"offerSpan\")\n get offerSpanYear() {\n return this.offerSpan.replace(/(\\D+)(\\d+)$/, (_, __, year) => year);\n }\n @computedFrom(\"offerData.credit\")\n get offerCredit() {\n const { credit: offerCredit = 0 } = this.offerData || {};\n return offerCredit;\n }\n @computedFrom(\"offerData.duration\")\n get offerDurationString() {\n const { duration: offerDurationString = \"\" } = this.offerData || {};\n return offerDurationString;\n }\n @computedFrom(\"offerDurationString\")\n get offerDurationMonths() {\n const [offerDurationMonthsString = \"\"] = \n this.offerDurationString.match(/^\\d+/);\n return offerDurationMonthsString ? Number(offerDurationMonthsString) : 0;\n }\n\n @computedFrom(\"offerActivationData.activatesData\")\n get activatesEdgeData() {\n const { \n activatesData: activatesEdgeData = null \n } = this.offerActivationData || null;\n return activatesEdgeData;\n }\n @computedFrom(\"activatesEdgeData.activatedOn\")\n get offerActivationDate() {\n const { activatedOn: offerActivatedOnMs = 0 } = this.activatesEdgeData || {};\n return computeAbsoluteTimeString({\n timeStampMs: offerActivatedOnMs,\n formatString: \"P\",\n localeCode: this.uiLocaleCode\n });\n }\n @computedFrom(\"activatesEdgeData.expiresOn\")\n get offerExpiryDate() {\n const { expiresOn: offerExpiresOnMs = 0 } = this.activatesEdgeData || {};\n return computeAbsoluteTimeString({\n timeStampMs: offerExpiresOnMs,\n formatString: \"P\",\n localeCode: this.uiLocaleCode\n });\n }\n\n @computedFrom(\"paymentIntentData.id\")\n get paymentIntentId() {\n const { id: paymentIntentId = \"\" } = this.paymentIntentData || {};\n return paymentIntentId;\n }\n\n @computedFrom(\"paymentIntentStatus\")\n get paymentStatusAlertClass() {\n switch (this.paymentIntentStatus) {\n case \"processing\":\n console.info(`Payment is being processed…`);\n return \"\";\n case \"succeeded\":\n console.info(`Payment succeeded!`);\n return \"uk-alert-success\";\n case \"requires_payment_method\":\n console.warn(`Payment failed. Please try again`);\n return \"uk-alert-danger\";\n case \"timedOut\":\n console.warn(`Payment status verification timed out.`);\n return \"uk-alert-warning\";\n default:\n console.error(`Payment failed unexpectedly!`);\n return \"uk-alert-danger\";\n }\n }\n @computedFrom(\"paymentIntentStatus\")\n get paymentStatusMainMessageI18nKey() {\n const status = this.paymentIntentStatus;\n switch (this.paymentIntentStatus) {\n case \"processing\":\n case \"succeeded\":\n case \"timedOut\":\n return `screens:paymentStatus.paymentStatuses.${status}.mainMessage`;\n case \"requires_payment_method\":\n return `screens:paymentStatus.paymentStatuses.failed.mainMessage`;\n default:\n return `screens:paymentStatus.paymentStatuses.failedUnexpectedly.mainMessage`;\n }\n }\n @computedFrom(\"paymentIntentStatus\")\n get paymentStatusActionMessageI18nKey() {\n const status = this.paymentIntentStatus;\n switch (this.paymentIntentStatus) {\n case \"processing\":\n case \"succeeded\":\n case \"timedOut\":\n return `screens:paymentStatus.paymentStatuses.${status}.actionMessage`;\n case \"requires_payment_method\":\n return `screens:paymentStatus.paymentStatuses.failed.actionMessage`;\n default:\n return `screens:paymentStatus.paymentStatuses.failedUnexpectedly.actionMessage`;\n }\n }\n @computedFrom(\"paymentIntentStatus\")\n get paymentStatusMainMessageI18nParams() {\n return i18nParams;\n }\n @computedFrom(\"paymentIntentStatus\")\n get paymentStatusActionMessageI18nParams() {\n return i18nParams;\n }\n\n\n @computedFrom(\"paymentIntentStatus\", \"offerActivationStatus\")\n get offerActivationAlertClass() {\n if (![\"succeeded\"].includes(this.paymentIntentStatus)) {\n return \"uk-alert\";\n }\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n console.info(`Offer is being activated…`);\n return `uk-alert`;\n case \"succeeded\":\n console.info(`Offer activation succeeded!`);\n return `uk-alert uk-alert-success`;\n case \"timedOut\":\n console.warn(`Offer activation timed out.`);\n return `uk-alert uk-alert-warning`;\n }\n }\n @computedFrom(\"paymentIntentStatus\", \"offerActivationStatus\")\n get offerActivationMainMessageI18nKey() {\n if (![\"succeeded\"].includes(this.paymentIntentStatus)) {\n return \"\";\n }\n const status = this.offerActivationStatus;\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n case \"succeeded\":\n case \"timedOut\":\n return `screens:paymentStatus.activationStatuses.${status}.mainMessage`;\n }\n }\n @computedFrom(\n \"paymentIntentStatus\",\n \"offerActivationStatus\",\n \"offerType\",\n \"offerTarget\"\n )\n get offerActivationActionMessageI18nKey() {\n if (![\"succeeded\"].includes(this.paymentIntentStatus)) {\n return \"\";\n }\n const status = this.offerActivationStatus;\n switch (this.offerActivationStatus) {\n case \"\":\n return \"\";\n case \"processing\":\n case \"timedOut\":\n return `screens:paymentStatus.activationStatuses.${status}.actionMessage`;\n case \"succeeded\": {\n let actionSubMessage;\n switch (this.offerType) {\n case \"flt\": // flat\n switch (this.offerTarget) {\n case \"dnr\": // donors\n actionSubMessage = \"donorOffer\";\n break;\n default:\n actionSubMessage = \"flatOffer\";\n }\n break;\n case \"crd\": // credit\n switch (this.offerTarget) {\n case \"dnr\": // donors\n actionSubMessage = \"donorOffer\";\n break;\n default:\n actionSubMessage = \"creditOffer\";\n }\n break;\n default:\n actionSubMessage = \"\";\n }\n return `screens:paymentStatus.activationStatuses.${status}` +\n `.actionMessage.${actionSubMessage}`;\n }\n }\n }\n @computedFrom(\"paymentIntentStatus\", \"offerActivationStatus\", \"offerType\")\n get offerActivationActionMessageI18nParams() {\n if (![\"succeeded\"].includes(this.paymentIntentStatus)) {\n return i18nParams;\n }\n switch (this.offerActivationStatus) {\n case \"\":\n case \"processing\":\n case \"timedOut\":\n return i18nParams;\n case \"succeeded\": {\n switch (this.offerType) {\n case \"crd\": // credit offer\n return {\n ...i18nParams,\n offerCredit: this.offerCredit\n };\n case \"flt\": // flat offer\n return {\n ...i18nParams,\n offerActivationDate: this.offerActivationDate,\n offerExpiryDate: this.offerExpiryDate,\n offerDurationMonths: this.offerDurationMonths\n };\n default:\n return i18nParams;\n }\n }\n }\n }\n\n\n // Core methods\n async updatePaymentIntentStatus() {\n let elapsedTimeMs = 0;\n do {\n const pollingIntervalMs = elapsedTimeMs < 2000 ? 500 :\n elapsedTimeMs < 4000 ? 1000 : 2000;\n\n // Get payment intent from corresponding client secret\n try {\n const { paymentIntent: paymentIntentData = null } = \n await this.stripe.retrievePaymentIntent(this.paymentIntentClientSecret);\n this.paymentIntentData = paymentIntentData;\n } catch(error) {\n console.warn(`Error getting payment intent`, error);\n await pause(pollingIntervalMs);\n elapsedTimeMs += pollingIntervalMs;\n continue;\n }\n const { \n status: paymentIntentStatus = \"processing\" \n } = this.paymentIntentData || {};\n this.paymentIntentStatus = paymentIntentStatus;\n\n // Wait for polling interval\n await pause(pollingIntervalMs);\n elapsedTimeMs += pollingIntervalMs;\n } while (\n [\"processing\"].includes(this.paymentIntentStatus) &&\n elapsedTimeMs < maxElapsedTimeMs\n );\n\n // Payment intent processing timed out\n if (elapsedTimeMs >= maxElapsedTimeMs) {\n console.warn(`Payment intent still being processed after ` +\n `${ms(elapsedTimeMs)}`);\n this.paymentIntentStatus = \"timedOut\";\n return;\n }\n\n // Payment intent succeeded -> activate offer\n if (this.paymentIntentStatus === \"succeeded\") {\n await this.updateOfferActivationStatus();\n }\n }\n\n async updateOfferActivationStatus() {\n this.offerActivationStatus = \"processing\";\n this.offerActivationData = await this.offerService.getOfferActivationStatus({ \n offerKey: this.offerKey,\n paymentId: this.paymentIntentId\n });\n this.offerActivationStatus = \n this.offerActivationData ? \"succeeded\" : \"timedOut\";\n }\n\n async cancelPayment() {\n try {\n await this.stripeService.cancelPaymentIntent({\n paymentIntentId: this.paymentIntentId,\n cancellationReason: \"abandoned\"\n });\n } catch(error) {\n console.warn(`Error canceling payment intent \"${this.paymentIntentId}\" ` +\n `for offer \"${this.offerKey}\"`, error);\n return;\n }\n console.info(`Canceled payment intent \"${this.paymentIntentId}\" ` +\n `for offer \"${this.offerKey}\"`);\n }\n\n retryPayment() {\n if (this.paymentIntentClientSecret) {\n console.info(`Retrying payment by navigating back to the checkout screen`);\n this.router.navigateToRoute(\"checkout\", {\n offerKey: this.offerKey,\n paymentIntentClientSecret: this.paymentIntentClientSecret\n });\n }\n }\n\n async cancelPaymentAndGoToDashboard() {\n await this.cancelPayment();\n this.router.navigateToRoute(\"dashboard\");\n }\n\n\n\n // Event handlers\n handleRunModeChangedEvent({ runMode = defaultRunMode }) {\n this.runMode = runMode;\n }\n\n}\n","// Define the view-model for the \"Settings\" screen\n\n// Import library modules\nimport { inject, computedFrom } from \"aurelia-framework\";\nimport { ValidateResult } from \"aurelia-validation\";\nimport { DialogService } from \"aurelia-dialog\";\nimport { I18N } from \"aurelia-i18n\";\n\n// Import parameter modules\nimport { tBindingBehavior as i18nParams } from \"parameters/i18n\";\n// import { localesData } from \"parameters/locale\";\n\n// Import dialog modules\nimport { ChangePasswordSuccess } from \"dialogs/change-password-success\";\nimport { ChangePassword } from \"dialogs/change-password\";\nimport { ConfirmClearLocalCache } from \"dialogs/confirm-clear-local-cache\";\n\n// Import service modules\nimport IndexService from \"services/index\";\nimport NotificationService from \"services/notification\";\nimport UserService from \"services/user\";\n\n\n// Export the \"Settings\" class\n@inject(\n DialogService, \n I18N,\n IndexService, \n NotificationService,\n UserService\n)\nexport class Settings {\n\n // Local attributes\n indexDocumentsCount = 0;\n i18nParams = i18nParams;\n serverErrorsData = [];\n databasesNames = [];\n\n constructor(\n dialogService, \n i18n,\n indexService, \n notificationService,\n userService\n ) {\n this.dialogService = dialogService;\n this.i18n = i18n;\n this.indexService = indexService;\n this.notificationService = notificationService;\n this.userService = userService;\n }\n\n async bind() {\n this.databasesNames = [ \"global\", this.indexService.userDatabaseName ];\n await this.updateIndexDocumentsCount();\n }\n\n\n // Getter and setter methods\n get userProfileMap() {\n return Object.entries(this.userService.profileData)\n .reduce((accProfileMap, [ propertyName, propertyValue ]) => {\n const i18nPropertyName = this.i18n.tr(`main:${propertyName}`);\n switch (propertyName) {\n case \"salutation\":\n case \"academicTitle\":\n case \"firstName\":\n case \"middleName\":\n case \"lastName\":\n case \"suffixName\":\n case \"displayName\":\n case \"birthYear\":\n case \"emailAddress\": {\n if (propertyValue) {\n accProfileMap.set(i18nPropertyName, propertyValue);\n }\n break;\n }\n case \"gender\": {\n if (propertyValue) {\n const i18nPropertyValue = \n this.i18n.tr(`enums:longPersonGender.${propertyValue}`);\n accProfileMap.set(i18nPropertyName, i18nPropertyValue);\n }\n break;\n }\n }\n return accProfileMap;\n }, new Map());\n }\n\n get userSettingsMap() {\n return Object.entries(this.userService.settingsData)\n .reduce((accUserSettingsMap, [ propertyName, propertyValue ]) => {\n switch (propertyName) {\n // case \"inLocaleCode\":\n // case \"uiLocaleCode\":\n // case \"outLocaleCode\": {\n // const i18nPropertyName = `main:${propertyName.replace(/Code$/, \"\")}`;\n // const localeData = localesData\n // .find(({ code: localeCode }) => localeCode === propertyValue);\n // if (localeData) {\n // const { languageCode, countryCode } = localeData;\n // const i18nKey = `enums:languageCodeToName.${languageCode}`;\n // const languageName = this.i18n.tr(i18nKey);\n // propertyValue = `${languageName} (${countryCode.toUpperCase()})`;\n // accUserSettingsMap.set(i18nPropertyName, propertyValue);\n // }\n // return accUserSettingsMap;\n // }\n case \"acceptServiceNotifications\": {\n const i18nPropertyName = [\n this.i18n.tr(`main:iAccept`),\n this.i18n.tr(`main:serviceNotifications`)\n ].join(\" \");\n propertyValue = \n this.i18n.tr(`enums:yesOrNo.${propertyValue ? \"yes\" : \"no\"}`);\n accUserSettingsMap.set(i18nPropertyName, propertyValue);\n return accUserSettingsMap;\n }\n default:\n return accUserSettingsMap;\n }\n }, new Map());\n }\n\n get otherSettingsMap() {\n const otherSettingsMap = new Map();\n const propertyName = this.i18n.tr(\"main:accountActivatedUponInvitation\");\n if (this.userService.invitingUserProfileData === null) {\n const propertyValue = this.i18n.tr(\"enums:yesOrNo.no\");\n otherSettingsMap.set(propertyName, propertyValue);\n } else {\n const { \n displayName: invitingUserDisplayName = \"???\" \n } = this.userService.invitingUserProfileData || {};\n const propertyValue = `${this.i18n.tr(\"enums:yesOrNo.yes\")} ` +\n `(${this.i18n.tr(\"main:byUser\")} \"${invitingUserDisplayName}\")`;\n otherSettingsMap.set(propertyName, propertyValue);\n }\n return otherSettingsMap;\n }\n\n @computedFrom(\"indexDocumentsCount\")\n get clearLocalCacheEnabled() {\n return this.indexDocumentsCount > 0;\n }\n\n\n // Core methods\n toggleHelpCanvas() {\n this.userService.conditionallyExtendSession(); // do not await\n }\n\n async changePassword() {\n const changePasswordDialogOpenResult = await this.dialogService.open({\n viewModel: ChangePassword,\n //keyboard: [\"Escape\"] // keys that close the dialog\n });\n const changePasswordDialogCloseResult =\n await changePasswordDialogOpenResult.closeResult;\n if (changePasswordDialogCloseResult.wasCancelled) {\n console.info(`User canceled the password change`);\n return;\n }\n const changePasswordSuccessDialogOpenResult =\n await this.dialogService.open({\n viewModel: ChangePasswordSuccess,\n //keyboard: [\"Enter\"] // keys that close the dialog\n });\n await changePasswordSuccessDialogOpenResult.closeResult;\n }\n\n async clearLocalCache() {\n const clearLocalCacheDialogOpenResult = await this.dialogService.open({\n viewModel: ConfirmClearLocalCache,\n //keyboard: [\"Escape\"] // keys that close the dialog\n });\n const clearLocalCacheDialogCloseResult =\n await clearLocalCacheDialogOpenResult.closeResult;\n if (clearLocalCacheDialogCloseResult.wasCancelled) {\n console.info(`User canceled clearing local cache`);\n return;\n }\n try {\n await Promise.all(this.indexService.databasesNames\n .map(async(databaseName) => {\n try {\n const deletedDocumentsCount = await this.indexService\n .deleteAllDatabaseDocuments({ databaseName });\n console.info(`Deleted ${deletedDocumentsCount} documents ` +\n `from index database \"${databaseName}\"`);\n } catch(error) {\n console.warn(`Error deleting all documents ` +\n `from index database \"${databaseName}\"`);\n }\n }));\n this.notificationService.showUINotification({\n message: \"notifications:localCacheCleared\",\n status: \"success\",\n group: \"private\"\n });\n } catch(error) {\n this.notificationService.showUINotification({\n message: \"notifications:localCacheClearingError\",\n status: \"failure\",\n group: \"private\"\n });\n }\n await this.updateIndexDocumentsCount();\n }\n\n async updateIndexDocumentsCount() {\n const { databasesNames } = this.indexService;\n const indexDocumentsCounts = await Promise.all(databasesNames\n .map(async(databaseName) => {\n try {\n return await this.indexService\n .getAllDatabaseDocumentsCount({ databaseName });\n } catch(error) {\n console.error(error);\n console.warn(`Error counting documents ` +\n `in index database \"${databaseName}\"\"`);\n return 0;\n }\n }));\n this.indexDocumentsCount = indexDocumentsCounts\n .reduce((accIndexDocumentsCount, indexDocumentsCount) => {\n return accIndexDocumentsCount + indexDocumentsCount;\n }, 0);\n }\n\n setServerError(errorKey) {\n this.serverErrorsData = [ this.serverErrorsData, {\n error: new ValidateResult(errorKey, this.rawFormData, \"server\", false)\n }];\n }\n\n clearServerErrors() {\n this.serverErrorsData = [];\n }\n\n}\n","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;","// Module\nvar code = \"\\n\";\n// Exports\nexport default code;"],"names":[],"sourceRoot":""}