Compare commits

..

197 Commits

Author SHA1 Message Date
gofnnp
c51b47955d test 2025-12-19 13:02:46 +04:00
a8240ed0db dev #15189 фикс мерджа 2023-09-12 15:10:58 +04:00
188f93e2b3 Merge branch 'tasty-coffee' of https://git.hlcompany.ru/git/usersite into tasty-coffee
# Conflicts:
#	angular/angular.json
#	angular/src/app/app.component.ts
#	angular/src/app/interface/data.ts
#	angular/src/app/presentation-options/default-option/components/navbar/navbar.component.html
#	angular/src/app/presentation-options/default-option/components/navbar/navbar.component.scss
#	angular/src/app/presentation-options/default-option/components/navbar/navbar.component.ts
#	angular/src/app/presentation-options/default-option/default-option.module.ts
#	angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.html
#	angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.scss
#	angular/src/app/presentation-options/default-option/pages/guest-card/guest-card.component.ts
#	angular/src/app/presentation-options/default-option/pages/login/login.component.html
#	angular/src/app/presentation-options/default-option/pages/login/login.component.scss
#	angular/src/app/services/auth.service.ts
#	angular/src/environments/environment.prod.ts
#	angular/src/environments/environment.ts
#	angular/src/manifest.webmanifest
#	angular/src/styles.scss
2023-09-12 14:42:41 +04:00
af56582aca dev #15189
сделал копию море для tasty coffee
2023-09-12 14:36:51 +04:00
f75d727c2d dev #15189
изменил на tasty coffee
2023-09-12 12:09:02 +04:00
756a033504 исправил кр код при авторизации 2023-09-05 15:04:50 +04:00
nikolay
29f8ca5230 dev #14384 Правка ошибок отображения WPA КофеЛайк: remove asterisk from name input 2023-08-21 09:46:05 +04:00
nikolay
a62f60df40 dev #14384 Правка ошибок отображения WPA КофеЛайк: remove banner 2023-08-18 09:06:12 +04:00
nikolay
ea66abfcd1 dev #14607 Море. Правки по сайту: add href to doc on word "условия" on login page 2023-08-11 10:06:12 +04:00
nikolay
172a21b7f7 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix description 2023-07-26 14:00:13 +04:00
nikolay
a1668de544 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix error handling 2023-07-26 10:30:07 +04:00
nikolay
3e9544ff96 dev #14384 Правка ошибок отображения WPA КофеЛайк: add "О программе лояльности" menu item and hide "О приложении" menu item 2023-07-24 12:08:26 +04:00
nikolay
86c913b4c1 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix app install button 2023-07-24 10:41:41 +04:00
nikolay
68dea47a27 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix level description 2023-07-21 14:21:51 +04:00
nikolay
2d3fc2f4ca dev #14384 Правка ошибок отображения WPA КофеЛайк: fix text styles 2023-07-20 13:32:05 +04:00
nikolay
59edb5939b dev #14384 Правка ошибок отображения WPA КофеЛайк: add exclamation mark 2023-07-19 16:58:10 +04:00
nikolay
9a6cecad00 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix description 2023-07-19 11:24:30 +04:00
nikolay
34447c0d8b dev #14384 Правка ошибок отображения WPA КофеЛайк: fix styles and level description 2023-07-19 11:23:31 +04:00
nikolay
1e62141a45 dev #14384 Правка ошибок отображения WPA КофеЛайк:
complete:
1. поменять банер на закругленный
2. добавить значок лупы для куар - как в приложении x5
3. Зеленый цвет для текста взять такой же как для вернхей панели
4. кнопку оценить так же цвет из верхней панели
5. в тексте процент бонусов и сумму покупок выделить тем же цветом что и Уровень и Кешбек около куара
6. текст сделать пошире, что бы на телефоне было 2-3 строки
2023-07-19 10:33:18 +04:00
nikolay
94b34e020e dev #14384 Правка ошибок отображения WPA КофеЛайк: fix qr code description 2023-07-17 12:01:12 +04:00
nikolay
56b1249e3d dev #14760 Кофелайк WPA динамическое сообщение в зависимости от уровня: add description table 2023-07-17 11:33:26 +04:00
nikolay
db666df676 dev #14384 Правка ошибок отображения WPA КофеЛайк: change background to white 2023-07-17 11:07:31 +04:00
nikolay
5cb88250c5 change colors to old version 2023-07-14 15:00:37 +04:00
7c48e1dc1e #14726
изменил фоновую картинку в карте валлет
2023-07-12 16:03:19 +04:00
nikolay
9d00a32d4f dev #14717 Добавить меню в андрод WPAL: remove back arrow from guest card 2023-07-11 15:48:02 +04:00
feeed2c640 Merge branch 'more' of https://git.hlcompany.ru/git/usersite into more 2023-07-11 14:24:09 +04:00
46059461db #14726
изменил картинки в карте валлет
2023-07-11 14:23:55 +04:00
nikolay
19577e272f dev #14717 Добавить меню в андрод WPA 2023-07-11 13:58:33 +04:00
nikolay
ea5b07964d dev #14716 Отображение в интерфейсе ошибок с бека 2023-07-11 12:18:15 +04:00
nikolay
d2669236ec return ruble sign in last order price 2023-07-06 13:22:43 +04:00
nikolay
14c2af863b dev #14607 Море. Правки по сайту: change size of agree info on login page 2023-07-06 11:16:58 +04:00
nikolay
50333514af dev #14384 Правка ошибок отображения WPA КофеЛайк: revert banner 2023-07-06 10:40:28 +04:00
nikolay
cc192a6b0f dev #14607 Море. Правки по сайту: fix input border color on hover 2023-07-05 16:17:48 +04:00
nikolay
1b8cb2b185 dev #14607 Море. Правки по сайту: remove date input blur 2023-07-05 15:38:42 +04:00
nikolay
eb15e47328 dev #14607 Море. Правки по сайту: set cursor position at beginning of date input on focus 2023-07-05 15:25:58 +04:00
nikolay
73de9ea6b7 dev #14607 Море. Правки по сайту: fix mask 2023-07-05 14:50:43 +04:00
nikolay
c1219e1637 dev #14607 Море. Правки по сайту: remove datepicker and add date mask 2023-07-05 14:21:19 +04:00
nikolay
5f522bad47 dev #14384 Правка ошибок отображения WPA КофеЛайк: some fixes 2023-07-04 15:53:49 +04:00
nikolay
9fd031f423 dev #14607 Море. Правки по сайту:
change agree info;
remove add to apple wallet notification;
fix apple wallet generation;
2023-07-04 15:45:43 +04:00
nikolay
7fe4b6de52 dev #14607 Море. Правки по сайту:
add agree information;
remove uncessary user update;
2023-07-04 15:03:57 +04:00
nikolay
55c98f5aa1 dev #14607 Море. Правки по сайту: remove menu item "about app" 2023-07-04 11:27:29 +04:00
nikolay
75ddd847f3 dev #14607 Море. Правки по сайту: add app to apple wallet on login and register 2023-07-04 10:41:12 +04:00
nikolay
252433e197 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix last purchase date format 2023-07-03 15:56:08 +04:00
nikolay
46bd1e6d2a dev #14384 Правка ошибок отображения WPA КофеЛайк: fix last purchase date format 2023-07-03 12:24:38 +04:00
nikolay
e5c2e0efe4 dev #14607 Море. Правки по сайту: add icons and fix some styles 2023-07-03 12:18:09 +04:00
nikolay
656e41b8cf dev #14384 Правка ошибок отображения WPA КофеЛайк: change user info data 2023-07-03 11:52:56 +04:00
nikolay
54795be75a dev #14607 Море. Правки по сайту: add registration form 2023-07-03 09:46:22 +04:00
nikolay
0e5e7e1d84 dev #14607 Море. Правки по сайту: fixes 2023-06-30 13:38:43 +04:00
nikolay
4c7b5437df dev #14607 Море. Правки по сайту: show all bonuses 2023-06-29 16:09:56 +04:00
nikolay
e640539d96 dev #14607 Море. Правки по сайту: fix notification and auth new_customer 2023-06-29 15:35:34 +04:00
nikolay
7808270bb4 dev #14607 Море. Правки по сайту: some fixes 2023-06-29 14:22:29 +04:00
nikolay
e12eb7a542 dev #14607 Море. Правки по сайту: add apple wallet and logo 2023-06-29 12:53:32 +04:00
nikolay
0abdb264f5 dev #14607 Море. Правки по сайту: change interface and api 2023-06-29 11:53:38 +04:00
nikolay
b7cc0ed72d dev #14384 Правка ошибок отображения WPA КофеЛайк:
fix header;
add link to banner;
fix last purchase loading;
2023-06-29 09:11:08 +04:00
tosandriy
06f668cd3d changed system id 2023-06-28 15:17:11 +04:00
b6483f78a7 dev #14600
изменил путь сборки
2023-06-28 13:17:36 +04:00
9b3488775c dev #14600
изменил названия для новой ветки
2023-06-28 11:56:16 +04:00
nikolay
e5ae004a29 dev #14384 Правка ошибок отображения WPA КофеЛайк: show only one message at a time and fix header 2023-06-27 16:01:12 +04:00
nikolay
142ece15de dev #14384 Правка ошибок отображения WPA КофеЛайк: add banner 2023-06-27 15:37:52 +04:00
nikolay
c9bcf3fc15 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix login form 2023-06-27 14:22:52 +04:00
nikolay
9e7394e848 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix last purchase 2023-06-26 10:40:10 +04:00
nikolay
8ea4a41786 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix last purchase 2023-06-26 10:24:18 +04:00
nikolay
7fa882e423 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix mistake 2023-06-23 14:36:24 +04:00
nikolay
03771d2012 dev #14384 Правка ошибок отображения WPA КофеЛайк: show info when wallet balance is negative 2023-06-23 13:33:02 +04:00
nikolay
3ff34525ed dev #14384 Правка ошибок отображения WPA КофеЛайк: fix logout and login, change ios header 2023-06-23 11:06:28 +04:00
nikolay
4e0a41b644 dev #14384 Правка ошибок отображения WPA КофеЛайк: add auth service to store information about user and fix getUserInfo 2023-06-23 09:32:20 +04:00
nikolay
e20cd30a54 dev #14562 Добавить меню для iOS WPA: fix z index of menu 2023-06-22 12:48:08 +04:00
nikolay
abdb5b790c dev #14562 Добавить меню для iOS WPA: remove back arrow on ios on guest card 2023-06-22 12:32:02 +04:00
nikolay
10e180613f dev #14562 Добавить меню для iOS WPA: remove install app 2023-06-22 11:56:00 +04:00
nikolay
6d55ca2b14 dev #14384 Правка ошибок отображения WPA КофеЛайк: check if app is in installed apps 2023-06-22 11:48:12 +04:00
nikolay
fcdb3d8fc7 dev #14384 Правка ошибок отображения WPA КофеЛайк: add getInstalledrelatedApps log 2023-06-22 11:35:06 +04:00
nikolay
0ae4e31cfd dev #14562 Добавить меню для iOS WPA 2023-06-22 11:09:52 +04:00
nikolay
50ae2b601e dev #14384 Правка ошибок отображения WPA КофеЛайк: fix install button 2023-06-21 11:41:50 +04:00
nikolay
798ec5e21a dev #14384 Правка ошибок отображения WPA КофеЛайк: change keydown to input on code enter listener 2023-06-21 10:27:05 +04:00
nikolay
15e9fe7ec4 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix sms enter code in safari 2023-06-21 10:06:11 +04:00
nikolay
26b2e8eb13 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix sms enter code on ios 2023-06-20 12:28:35 +04:00
nikolay
7b5200d9f8 auto fill sms 2023-06-20 10:12:32 +04:00
nikolay
d66f72dc2e dev #14537 Добавить кнопку подписки на уведомления CL 2023-06-19 10:04:45 +04:00
nikolay
0ca219c7fa dev #14525 Заменить текст WPA CoffeeLike 2023-06-15 17:09:03 +04:00
nikolay
1a6345431d change firebase config 2023-06-15 16:49:41 +04:00
nikolay
bef4ed1a48 dev #14494 WPA CoffeeLike изменить апи для получения последней транзакции 2023-06-15 15:49:33 +04:00
nikolay
eef97fd35f dev #14494 WPA CoffeeLike изменить апи для получения последней транзакции 2023-06-15 15:40:19 +04:00
nikolay
a8062667df fix jenkinsfile 2023-06-15 15:07:28 +04:00
nikolay
46d10e50ea change jenkins node 2023-06-15 14:35:43 +04:00
nikolay
ccaa41ddf9 dev #14490 WPA CoffeeLike добавление в wallet вместо установки для iOS: fix url 2023-06-15 11:44:35 +04:00
nikolay
0e38321526 dev #14507 Авто подписка на уведомления для андроид WPA Кофелайк 2023-06-14 15:36:02 +04:00
nikolay
4e52e180d8 add logging 2023-06-14 09:18:49 +04:00
nikolay
30854f4cb9 dev #14490 WPA CoffeeLike добавление в wallet вместо установки для iOS 2023-06-13 15:31:04 +04:00
nikolay
425ef99af2 dev #14495 WPA CoffeeLike добавить ссылку на Правила 2023-06-13 13:33:35 +04:00
nikolay
b29f2e1fdf dev #14490 WPA CoffeeLike добавление в wallet вместо установки для iOS 2023-06-09 12:18:09 +04:00
nikolay
5c3e4a0368 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix dependencies 2023-05-31 16:07:16 +04:00
nikolay
077b60fc87 dev #14384 Правка ошибок отображения WPA КофеЛайк: upgrade angular to 15 2023-05-31 15:57:54 +04:00
nikolay
e06d703b69 dev #14384 Правка ошибок отображения WPA КофеЛайк: change build command 2023-05-31 09:40:59 +04:00
nikolay
cfe370e3e3 Merge branch 'coffee-like-test' of https://git.hlcompany.ru/git/usersite into coffee-like-test 2023-05-29 16:16:39 +04:00
nikolay
e3fd36b881 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix issue 2023-05-29 16:16:12 +04:00
1b1dcd9b50 Merge branch 'coffee-like-test' of https://git.hlcompany.ru/git/usersite into coffee-like-test 2023-05-26 10:42:13 +04:00
bb7e39f885 Поменял логотип для светлой темы 2023-05-26 10:41:49 +04:00
nikolay
b31bd30161 dev #14305 Изменение дизайна WPA Кофе-лайка: change styles 2023-05-25 16:57:04 +04:00
nikolay
7d098d1798 dev #14305 Изменение дизайна WPA Кофе-лайка: change styles 2023-05-25 16:43:27 +04:00
nikolay
7bade7caea dev #14305 Изменение дизайна WPA Кофе-лайка: fix infinity loading when login (typescript error) 2023-05-23 12:36:10 +04:00
238b70e95b dev #14338
Правка стилей
2023-05-20 21:51:07 +04:00
877cf018af dev #14338
добавил кнопку установки pwa
2023-05-20 16:09:36 +04:00
nikolay
24edd456db dev #14328 Добавить префикс в формирование QR: remove + 2023-05-18 15:33:22 +04:00
nikolay
cff512c561 Merge branch 'coffee-like-test' of https://git.hlcompany.ru/git/usersite into coffee-like-test 2023-05-18 09:55:11 +04:00
nikolay
6b60c4d8c4 dev #14328 Добавить префикс в формирование QR: change phone value in qr 2023-05-18 09:50:20 +04:00
04884bd0d9 Merge branch 'coffee-like-test' of https://git.hlcompany.ru/git/usersite into coffee-like-test 2023-05-17 12:12:41 +04:00
6c283812e7 dev #14318
добавил регистрацию юзера в айкокард, если его нет
2023-05-17 12:12:25 +04:00
nikolay
c5e4f3c229 dev #14305 Изменение дизайна WPA Кофе-лайка: block user with no info 2023-05-17 11:42:50 +04:00
nikolay
2fa05b391e dev #14305 Изменение дизайна WPA Кофе-лайка: change loyality program page 2023-05-16 15:38:21 +04:00
nikolay
ff9e04ecbd dev #14305 Изменение дизайна WPA Кофе-лайка: fix manifest 2023-05-16 11:53:23 +04:00
nikolay
28ab33248d dev #14305 Изменение дизайна WPA Кофе-лайка: fix manifest 2023-05-16 11:51:02 +04:00
nikolay
659e9d50bf dev #14305 Изменение дизайна WPA Кофе-лайка: fix manifest 2023-05-16 11:45:31 +04:00
nikolay
43980516ff dev #14305 Изменение дизайна WPA Кофе-лайка: change icons 2023-05-16 11:40:12 +04:00
nikolay
ca0fa2bfa6 dev #14305 Изменение дизайна WPA Кофе-лайка: change loading 2023-05-16 11:04:25 +04:00
nikolay
b81ee7f713 dev #14305 Изменение дизайна WPA Кофе-лайка: change qr code color to white 2023-05-16 09:11:28 +04:00
nikolay
044144735d dev #14305 Изменение дизайна WPA Кофе-лайка: change qr code color 2023-05-15 14:32:32 +04:00
nikolay
6feeec80f2 dev #14305 Изменение дизайна WPA Кофе-лайка: round bonuses count 2023-05-15 13:45:49 +04:00
e062f05f36 Merge remote-tracking branch 'remotes/origin/coffee-like' into coffee-like-test 2023-05-15 12:20:24 +04:00
bf3fd7e0a4 dev #13995
убарл очепятку
2023-05-15 12:10:26 +04:00
37ad910e75 dev #13995
добавил обновление имени и даты рождения(заглшка, чтобы работал костыль в СА) при авторизации
2023-05-15 12:08:22 +04:00
4a492f16e8 dev #13995
перенес изменения Николая из основной директории в дефолтный вариант представления
2023-05-15 11:52:27 +04:00
1e7175e3b5 dev #13995
сделал тестовую ветку , поменял в ней названия
2023-05-15 10:43:44 +04:00
be7c62a12b dev #13995
поменял иконки
2023-05-15 10:30:20 +04:00
6a075ec773 Merge branch 'coffee-like' of https://git.hlcompany.ru/git/usersite into coffee-like 2023-05-12 20:18:37 +04:00
86ac20f5e5 dev #13995
изменил структуру под аб тестирование, сделал 2 различных вариант (один дефолтный, другой с красным текстом)
модули загружаются лениво, для оптимизации загрузки приложения
пофиксил подсчет суммы транзакций за периоды
2023-05-12 20:10:06 +04:00
nikolay
2131e052c6 issue 14305: change styles 2023-05-12 16:21:46 +04:00
nikolay
787bc65691 change styles 2023-05-12 15:10:44 +04:00
04ecbb837c dev #13995
Исправил баги, доработал таймер для отправки повторного кода и сделал обработку ошибок при авторизации
2023-05-10 13:11:41 +04:00
77ecb32411 dev #13995
правка
2023-05-10 11:07:00 +04:00
9e1cad31f3 dev #13995
поменял версию
2023-05-10 02:40:47 +04:00
7c3a4168c4 dev #13995
поменял фавикон и другие доработки
2023-05-10 02:39:33 +04:00
76f1799b22 dev #14249
Информация по транзакциям
2023-05-10 02:38:17 +04:00
42b46bc478 dev #13995 2023-05-02 11:39:19 +04:00
af60b55e94 dev #13995
припилил СА, iikocard (но пока только customer_info), доработки
2023-04-25 19:10:14 +04:00
1da7556aed dev #13995
первоначальная верстка
2023-04-18 15:05:02 +04:00
f460954bc8 initial 2023-04-06 23:48:00 +04:00
7a9f9fc872 dev #13951
Исправил ошибки у новых пользователей
2023-03-30 12:43:42 +04:00
7bb05324a9 dev #13951
сделал суммирование бонусов со всех бонусных программ
2023-03-20 23:30:20 +04:00
5e3b211778 dev #13882
правки
2023-03-08 02:04:54 +04:00
e75999a3c1 dev #13882
правки
2023-03-08 02:02:21 +04:00
f8f500f612 dev #13882
правки
2023-03-08 01:42:44 +04:00
72808c3c7c dev #13882
правки
2023-03-08 01:36:27 +04:00
efb8095e2f dev #13882
правки
2023-03-08 01:17:48 +04:00
699db43b39 dev #13882
правки
2023-03-08 01:11:58 +04:00
5152d9ad30 dev #13882
правки
2023-03-08 00:29:44 +04:00
da340475e0 support #13704
поменял картинки
2023-02-19 23:46:28 +04:00
ef8ac5d24d support #13704
поменял картинку в apple wallet
2023-02-05 21:01:20 +04:00
fa5d29e517 support #13704
правка
2023-02-05 20:55:56 +04:00
1a5fa44abc support #13704
поменял картинки
2023-02-05 20:47:41 +04:00
Kataev Denis
bedda578c8 support #13704
поменял фавикон
2023-02-01 03:35:31 +04:00
Kataev Denis
44cf8d5074 support #13704
поменял картинку бабочки в манифесте
2023-02-01 03:29:49 +04:00
Kataev Denis
ecdd4f103e support #13704
поменял картинку бабочки
2023-02-01 03:11:59 +04:00
c0387d967d dev #13538
правка
2023-01-13 03:22:36 +04:00
b33ed15666 dev #13538
правка
2023-01-13 03:13:06 +04:00
c7ade7515c dev #13538
правка
2023-01-13 03:07:39 +04:00
e4bb1d8947 dev #13538
правка
2023-01-13 03:05:50 +04:00
c71e2d4c48 dev #13538
правка
2023-01-13 03:02:49 +04:00
88b0d92300 dev #13538
правка
2023-01-12 19:37:31 +04:00
e570e82f90 dev #13538
правка
2023-01-12 19:27:34 +04:00
85174c1fab dev #13538
правка
2023-01-12 19:22:10 +04:00
7a895c76a6 dev #13538
правка
2023-01-12 02:41:31 +04:00
dbf01e4fc5 dev #13538
правка
2023-01-12 02:38:24 +04:00
227392d7f6 dev #13538
правка
2023-01-12 02:35:56 +04:00
f8f2fe4673 dev #13538
правка
2023-01-12 02:27:14 +04:00
4d16fa4184 dev #13538
правка
2023-01-12 02:04:56 +04:00
5e69d6e5c5 dev #13538
перенес кнопку, вынес функционал в директиву
2023-01-12 01:57:32 +04:00
pumpurumer
a0de9cbb61 bump version 2022-10-27 12:34:25 +04:00
pumpurumer
192a7a5d2f поменял секреты на нормальные 2022-10-19 12:47:16 +04:00
dfd596cd56 dev #12797
Правки
2022-10-16 22:29:36 +04:00
b92b1e73ce dev #12797
Добавил обновление карты при обновлении данных пользователя
2022-10-16 22:24:38 +04:00
8ca151574a dev #12797
Доработки на фронте
2022-10-16 22:05:12 +04:00
Kataev Denis
9f4447fa79 package-lock 2022-10-12 11:46:37 +04:00
197c84f29c dev #12797
изменил расположение директорий и изменил файлы Jenkinsfile и gitignore
2022-10-11 10:40:59 +04:00
Kataev Denis
29d274e05b Merge branch 'fashion-logica' of https://git.hlcompany.ru/git/usersite into fashion-logica 2022-10-11 09:55:20 +04:00
Kataev Denis
e558fcabc0 dev #12797
генерация карты
2022-10-11 09:55:01 +04:00
1ee13ab73d dev #12618
поправил проверку на существование в реферальной системе
2022-09-30 13:07:02 +04:00
4414480d6e dev #12425
Поправил сохранения токенов для пушей
2022-09-30 13:02:57 +04:00
2ca121cbac Merge branch 'fashion-logica' of https://git.hlcompany.ru/git/usersite into fashion-logica 2022-09-29 21:47:34 +04:00
25a5db7337 dev #12401
правки по реферальной системе и версии приложения
2022-09-29 21:46:49 +04:00
Kataev Denis
134ee3f59d dev #12425
Поменял учетные данные
2022-09-29 18:41:33 +04:00
Kataev Denis
914b1ec5ed dev #12401
вернул как было
2022-09-29 10:32:06 +04:00
Kataev Denis
86eaca953e dev #12401
Страница 404 доработка
2022-09-29 10:15:21 +04:00
Kataev Denis
a22479dffd Merge branch 'fashion-logica' of https://git.hlcompany.ru/git/usersite into fashion-logica 2022-09-29 10:09:25 +04:00
Kataev Denis
bb455b2c45 dev #12401
Страница 404 и правки
2022-09-29 10:09:15 +04:00
pumpurumer
9b64746c2e test commit 2022-09-28 15:07:39 +04:00
pumpurumer
34080a0ee0 build commit 2022-09-28 14:58:06 +04:00
pumpurumer
ff0f8d6fad rebuild 2022-09-28 14:54:07 +04:00
pumpurumer
988a05e94d rebuild commit 2022-09-28 14:35:27 +04:00
pumpurumer
5b9be577e7 rebuild commit 2022-09-28 14:34:31 +04:00
pumpurumer
615f708600 rebuild commit 2022-09-28 14:29:32 +04:00
pumpurumer
ba71eee97f rebuild commit 2022-09-28 14:28:29 +04:00
pumpurumer
06e0b8efe1 rebuild commit 2022-09-28 14:27:33 +04:00
pumpurumer
7a490e4599 build commit 2022-09-28 14:25:53 +04:00
pumpurumer
8b6af5fa21 build commit 2022-09-28 14:25:16 +04:00
55486de524 dev #12738 2022-09-27 22:53:36 +04:00
Kataev Denis
836af4e1ae dev #12738 2022-09-27 20:15:18 +04:00
Kataev Denis
9352d06e73 dev #12738
Сделал бранчу и добавил в нее jenkinsfile
2022-09-27 14:17:17 +04:00
299 changed files with 38405 additions and 29358 deletions

View File

@ -1,16 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR

84
.gitignore vendored
View File

@ -1,42 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output # Compiled output
/dist angular/dist
/tmp angular/tmp
/out-tsc angular/out-tsc
/bazel-out angular/bazel-out
# Node # Node
/node_modules angular/node_modules
npm-debug.log angular/npm-debug.log
yarn-error.log angular/yarn-error.log
# IDEs and editors # IDEs and editors
.idea/ angular/.idea/
.project angular/.project
.classpath angular/.classpath
.c9/ angular/.c9/
*.launch angular/*.launch
.settings/ angular/.settings/
*.sublime-workspace angular/*.sublime-workspace
# Visual Studio Code # Visual Studio Code
.vscode/* angular/.vscode/*
!.vscode/settings.json angular/!.vscode/settings.json
!.vscode/tasks.json angular/!.vscode/tasks.json
!.vscode/launch.json angular/!.vscode/launch.json
!.vscode/extensions.json angular/!.vscode/extensions.json
.history/* angular/.history/*
# Miscellaneous # Miscellaneous
/.angular/cache angular/.angular/cache
.sass-cache/ angular/.sass-cache/
/connect.lock angular/connect.lock
/coverage angular/coverage
/libpeerconnection.log angular/libpeerconnection.log
testem.log angular/testem.log
/typings angular/typings
# System files # System files
.DS_Store angular/.DS_Store
Thumbs.db angular/Thumbs.db

View File

@ -1,4 +0,0 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

20
.vscode/launch.json vendored
View File

@ -1,20 +0,0 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "pwa-chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored
View File

@ -1,42 +0,0 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

30
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,30 @@
env.HL_BUILD_MODE = "jenkins"
node('Palladium'){
stage('get new version to repo') {
checkout scm
if (lastCommitIsBumpCommit()) {
currentBuild.result = 'ABORTED'
error('Последний коммит - результат сборки jenkins')
}
//sh "git submodule update --init --recursive"
//sh "git submodule update --remote --merge"
}
stage("build and publish"){
dir('angular'){
sh label: '', script: 'npm i'
sh label: '', script: 'npm run build'
}
}
}
private boolean lastCommitIsBumpCommit() {
lastCommit = sh([script: 'git log -1', returnStdout: true])
if (lastCommit.contains("Author: jenkins")) {
return true
} else {
return false
}
}

View File

@ -1,122 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"fashion-logica": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "/var/www/lk/fashion-logica",
"baseHref": "/fashion-logica/",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
"src/manifest.webmanifest",
"src/firebase-messaging-sw.js"
],
"styles": [
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css",
"node_modules/ngx-sharebuttons/themes/modern.scss",
"node_modules/ngx-sharebuttons/themes/material.scss",
"src/styles.scss"
],
"scripts": [],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "4mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "fashion-logica:build:production"
},
"development": {
"browserTarget": "fashion-logica:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "fashion-logica:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/firebase-messaging-sw.js",
"src/assets",
"src/manifest.webmanifest"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@ -1,16 +1,16 @@
# Editor configuration, see https://editorconfig.org # Editor configuration, see https://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.ts] [*.ts]
quote_type = single quote_type = single
[*.md] [*.md]
max_line_length = off max_line_length = off
trim_trailing_whitespace = false trim_trailing_whitespace = false

View File

@ -1,27 +1,27 @@
# FashionLogica # TastyCoffee
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.6.
## Development server ## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
## Code scaffolding ## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build ## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
## Running unit tests ## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests ## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
## Further help ## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

131
angular/angular.json Normal file
View File

@ -0,0 +1,131 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"tasty-coffee": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"i18n": {
"sourceLocale": "ru-RU"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "/var/www/html/lk.crm4retail.ru/tasty-coffee",
"baseHref": "/",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets",
"src/manifest.webmanifest",
"src/firebase-messaging-sw.js",
"src/sw-master.js",
"src/sw-custom.js"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css",
"node_modules/ngx-sharebuttons/themes/modern.scss",
"node_modules/ngx-sharebuttons/themes/material.scss",
"src/styles.scss"
],
"scripts": [],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "4mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "tasty-coffee:build:production"
},
"development": {
"browserTarget": "tasty-coffee:build:development",
"proxyConfig": "proxy.confi.json"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "tasty-coffee:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/firebase-messaging-sw.js",
"src/assets",
"src/manifest.webmanifest"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.scss"
],
"scripts": []
}
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@ -1,44 +1,44 @@
// Karma configuration file, see link for more information // Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html // https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: '', basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'], frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [ plugins: [
require('karma-jasmine'), require('karma-jasmine'),
require('karma-chrome-launcher'), require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'), require('karma-jasmine-html-reporter'),
require('karma-coverage'), require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma') require('@angular-devkit/build-angular/plugins/karma')
], ],
client: { client: {
jasmine: { jasmine: {
// you can add configuration options for Jasmine here // you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false` // for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321` // or set a specific seed with `seed: 4321`
}, },
clearContext: false // leave Jasmine Spec Runner output visible in browser clearContext: false // leave Jasmine Spec Runner output visible in browser
}, },
jasmineHtmlReporter: { jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces suppressAll: true // removes the duplicated traces
}, },
coverageReporter: { coverageReporter: {
dir: require('path').join(__dirname, './coverage/card-project'), dir: require('path').join(__dirname, './coverage/card-project'),
subdir: '.', subdir: '.',
reporters: [ reporters: [
{ type: 'html' }, { type: 'html' },
{ type: 'text-summary' } { type: 'text-summary' }
] ]
}, },
reporters: ['progress', 'kjhtml'], reporters: ['progress', 'kjhtml'],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['Chrome'], browsers: ['Chrome'],
singleRun: false, singleRun: false,
restartOnFileChange: true restartOnFileChange: true
}); });
}; };

View File

@ -1,30 +1,30 @@
{ {
"$schema": "./node_modules/@angular/service-worker/config/schema.json", "$schema": "./node_modules/@angular/service-worker/config/schema.json",
"index": "/index.html", "index": "/index.html",
"assetGroups": [ "assetGroups": [
{ {
"name": "app", "name": "app",
"installMode": "prefetch", "installMode": "prefetch",
"resources": { "resources": {
"files": [ "files": [
"/favicon.ico", "/favicon.ico",
"/index.html", "/index.html",
"/manifest.webmanifest", "/manifest.webmanifest",
"/*.css", "/*.css",
"/*.js" "/*.js"
] ]
} }
}, },
{ {
"name": "assets", "name": "assets",
"installMode": "lazy", "installMode": "lazy",
"updateMode": "prefetch", "updateMode": "prefetch",
"resources": { "resources": {
"files": [ "files": [
"/assets/**", "/assets/**",
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
] ]
} }
} }
] ]
} }

File diff suppressed because it is too large Load Diff

63
angular/package.json Normal file
View File

@ -0,0 +1,63 @@
{
"name": "tasty-coffee",
"version": "0.0.2",
"scripts": {
"ng": "ng",
"start": "ng serve --host 192.168.0.58",
"build": "ng build --aot --configuration production --output-hashing none",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^15.2.9",
"@angular/cdk": "^15.2.9",
"@angular/common": "^15.2.9",
"@angular/compiler": "^15.2.9",
"@angular/core": "^15.2.9",
"@angular/fire": "^7.5.0",
"@angular/forms": "^15.2.9",
"@angular/material": "^15.2.9",
"@angular/material-moment-adapter": "^15.2.9",
"@angular/platform-browser": "^15.2.9",
"@angular/platform-browser-dynamic": "^15.2.9",
"@angular/router": "^15.2.9",
"@angular/service-worker": "^15.2.9",
"@fortawesome/angular-fontawesome": "^0.12.1",
"@fortawesome/fontawesome-svg-core": "^6.2.0",
"@fortawesome/free-brands-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"@types/uuid": "^8.3.4",
"@types/web": "^0.0.99",
"angular-moment-timezone": "^1.7.1",
"angular2-text-mask": "^9.0.0",
"barcode-2-svg": "^0.3.3",
"firebase": "^9.9.3",
"google-libphonenumber": "^3.2.30",
"jsbarcode": "^3.11.5",
"libphonenumber-js": "^1.10.28",
"ng-qrcode": "^8.0.1",
"ngx-mat-intl-tel-input": "^5.0.0",
"ngx-sharebuttons": "^11.0.0",
"primeicons": "^5.0.0",
"primeng": "^15.4.1",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"uuid": "^8.3.2",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.2.8",
"@angular/cli": "~15.2.8",
"@angular/compiler-cli": "^15.2.9",
"@types/google-libphonenumber": "^7.4.23",
"@types/jasmine": "~4.0.0",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"typescript": "4.8"
}
}

38
angular/proxy.confi.json Normal file
View File

@ -0,0 +1,38 @@
{
"/api": {
"target": "https://apple-push-notifications.it-retail.tech/apns/api",
"secure": false,
"pathRewrite": {
"^/api": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/icard-proxy": {
"target": "https://sakura.lk.crm4retail.ru/api/icard-proxy",
"secure": false,
"pathRewrite": {
"^/icard-proxy": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/static": {
"target": "https://sakura.lk.crm4retail.ru/static",
"secure": false,
"pathRewrite": {
"^/static": ""
},
"changeOrigin": true,
"logLevel": "debug"
},
"/it-retail": {
"target": "https://sakura.lk.crm4retail.ru/it-retail",
"secure": false,
"pathRewrite": {
"^/it-retail": ""
},
"changeOrigin": true,
"logLevel": "debug"
}
}

View File

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MainComponent } from './pages/main/main.component';
import { AuthGuard } from './guards/auth-guard.guard';
const routes: Routes = [
{
path: '',
loadChildren: () => null as any,
// component: GuestCardComponent,
// canActivate: [AuthGuard]
},
// { path: '**', component: NotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,2 @@
<router-outlet></router-outlet>
<p-toast position="top-center"></p-toast>

View File

@ -1,35 +1,35 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule RouterTestingModule
], ],
declarations: [ declarations: [
AppComponent AppComponent
], ],
}).compileComponents(); }).compileComponents();
}); });
it('should create the app', () => { it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance; const app = fixture.componentInstance;
expect(app).toBeTruthy(); expect(app).toBeTruthy();
}); });
it(`should have as title 'fashion-logica'`, () => { it(`should have as title 'tasty-coffee'`, () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance; const app = fixture.componentInstance;
expect(app.title).toEqual('fashion-logica'); expect(app.title).toEqual('tasty-coffee');
}); });
it('should render title', () => { it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges(); fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement; const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('fashion-logica app is running!'); expect(compiled.querySelector('.content span')?.textContent).toContain('tasty-coffee app is running!');
}); });
}); });

View File

@ -0,0 +1,64 @@
import { Component, OnInit } from '@angular/core';
import { RouteConfigLoadStart, Router } from '@angular/router';
import { AuthService } from './services/auth.service';
import { CookiesService } from './services/cookies.service';
import { JsonrpcService, RpcService } from './services/jsonrpc.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
title = 'Tasty Coffee';
constructor(
private router: Router,
private cookiesService: CookiesService,
private jsonRpcService: JsonrpcService,
private authService: AuthService,
) {
this.router.events.subscribe((x) => {
if (x instanceof RouteConfigLoadStart && x.route.path === '') {
x.route.loadChildren = () => {
switch (this.cookiesService.getItem('presentation-option')) {
case 'first':
return import(
'./presentation-options/first-option/first-option.module'
).then((mod) => mod.FirstOptionModule);
default:
return import(
'./presentation-options/default-option/default-option.module'
).then((mod) => mod.DefaultOptionModule);
}
};
}
});
}
getAdditionalInfo() {
return this.jsonRpcService.rpc(
{
method: 'getAdditionalInfo',
params: [],
},
RpcService.authService,
true
);
}
ngOnInit(): void {
if (this.authService.authorized) {
this.authService.getUserInfo();
}
this.getAdditionalInfo().subscribe({
next: (value) => {
this.cookiesService.setCookie('presentation-option', value?.data?.interface_id || 'default')
},
error: (err) => {
console.error(err);
}
})
}
}

View File

@ -0,0 +1,93 @@
import { OrderStatus, Page, PageCode, lvlPeriod } from "./interface/data";
export const PageList: Page[] = [
{
code: PageCode.Auth,
name: 'Вход',
resName: 'auth',
onSideBar: false,
},
{
code: PageCode.Orders,
name: 'Заказы',
resName: 'orders',
onSideBar: true,
},
];
export const PageListWithBonus: Page[] = [
{
code: PageCode.Auth,
name: 'Вход',
resName: 'auth',
onSideBar: false,
},
{
code: PageCode.BonusProgram,
name: 'Ваша карта лояльности',
description: '',
resName: 'bonus-program',
onSideBar: true,
},
{
code: PageCode.Orders,
name: 'Ваши чеки',
description: '',
resName: 'orders',
onSideBar: true,
},
{
code: PageCode.UserData,
name: 'Заполнить анкету',
description: '',
resName: 'user-data',
onSideBar: true
},
{
code: PageCode.RefSystem,
name: 'Пригласить друга',
description: '',
resName: 'ref-system',
onSideBar: true,
}
];
export const orderStatuses: OrderStatus = {
'Cancelled': 'Отменен',
'InProcessing': 'В обработке',
'Unconfirmed': 'Принят',
'WaitCooking': 'Принят',
'ReadyForCooking': 'Принят',
'CookingStarted': 'Готовится',
'CookingCompleted': 'Приготовлен',
'Waiting': 'В пути',
'OnWay': 'В пути',
'Delivered': 'Выполнен',
'Closed': 'Выполнен',
};
export const lvlPeriods: lvlPeriod[] = [
{
percent: 3,
start: 0,
end: 1600,
color: '#f2c94c'
},
{
percent: 6,
start: 1601,
end: 3600,
color: '#f2994a'
},
{
percent: 10,
start: 3601,
end: 8600,
color: '#6fcf97'
},
{
percent: 15,
start: 8601,
color: '#6fcf97'
},
]

View File

@ -0,0 +1,121 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MainComponent } from './pages/main/main.component';
import { NavbarComponent } from './components/navbar/navbar.component';
import { CardComponent } from './components/card/card.component';
import { InputMaskModule } from 'primeng/inputmask';
import { AuthComponent } from './pages/account/auth/auth.component';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AccountComponent } from './pages/account/account.component';
import { ExitComponent } from './components/exit/exit.component';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DialogService } from 'primeng/dynamicdialog';
import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component';
import { OrderInfoComponent } from './components/order-info/order-info.component';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireMessagingModule } from '@angular/fire/compat/messaging';
import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api';
import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component';
import { UserDataComponent } from './pages/account/user-data/user-data.component';
import { RefSystemComponent } from './pages/account/ref-system/ref-system.component';
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareIconsModule } from 'ngx-sharebuttons/icons';
import { MessagingService } from './services/messaging.service';
import { NotFoundComponent } from './pages/not-found/not-found.component';
import { MatIconModule } from '@angular/material/icon';
import { GuestCardComponent } from './pages/guest-card/guest-card.component';
import { QrCodeModule } from 'ng-qrcode';
import { AccordionComponent } from './components/accordion/accordion.component';
import { LastOrderComponent } from './components/last-order/last-order.component';
import { InviteFriendsComponent } from './components/invite-friends/invite-friends.component';
import { FooterComponent } from './components/footer/footer.component';
import { SocialMediaButtonsComponent } from './components/social-media-buttons/social-media-buttons.component';
import { LoginComponent } from './pages/login/login.component';
import { NgxMatIntlTelInputComponent } from 'ngx-mat-intl-tel-input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import {
MAT_BOTTOM_SHEET_DATA,
MatBottomSheetModule,
MatBottomSheetRef,
} from '@angular/material/bottom-sheet';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { DirectivesModule } from './directives/directives.module';
@NgModule({
declarations: [
AppComponent,
NavbarComponent,
MainComponent,
CardComponent,
AuthComponent,
AccountComponent,
ExitComponent,
BonusProgramComponent,
OrderInfoComponent,
FooterButtonsComponent,
UserDataComponent,
RefSystemComponent,
NotFoundComponent,
GuestCardComponent,
AccordionComponent,
LastOrderComponent,
InviteFriendsComponent,
FooterComponent,
SocialMediaButtonsComponent,
LoginComponent,
// FocusNextInputDirective,
LoyalityProgramComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
InputMaskModule,
ProgressSpinnerModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
BrowserModule,
ServiceWorkerModule.register('/sw-master.js', {
enabled: environment.production,
// Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000',
}),
AngularFireModule.initializeApp(environment.firebase),
AngularFireMessagingModule,
ToastModule,
ReactiveFormsModule,
ShareButtonsModule.withConfig({
debug: true,
}),
ShareIconsModule,
MatIconModule,
QrCodeModule,
NgxMatIntlTelInputComponent,
MatFormFieldModule,
MatInputModule,
MatSnackBarModule,
MatBottomSheetModule,
MatProgressSpinnerModule,
DirectivesModule,
],
providers: [
DialogService,
MessageService,
MessagingService,
{ provide: MatBottomSheetRef, useValue: {} },
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: {} },
],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@ -0,0 +1,7 @@
<div class="tab">
<input [id]="'tab-' + guid" type="checkbox" name="tabs" />
<label [for]="'tab-' + guid">{{ header }}</label>
<div class="tab-content">
<ng-content></ng-content>
</div>
</div>

View File

@ -0,0 +1,85 @@
:host {
width: 100%;
}
.tab {
position: relative;
margin-bottom: 1px;
width: 100%;
color: var(--text-color);
overflow: hidden;
border-bottom: solid 1px #bdbdbd;
}
.tab input {
position: absolute;
opacity: 0;
z-index: -1;
}
.tab label {
display: block;
padding: 20px 26px;
position: relative;
background: #ffffff00;
font-weight: 400;
font-size: 12px;
line-height: 14px;
letter-spacing: -0.5px;
cursor: pointer;
}
// .tab label:before {
// display: block;
// content: " ";
// padding-top: 5px;
// }
.tab-content {
max-height: 0;
overflow: hidden;
background: #292929;
-webkit-transition: all 0.35s;
-o-transition: all 0.35s;
transition: all 0.35s;
border-top: solid 1px #bdbdbd;
font-style: normal;
font-weight: 400;
font-size: 12px;
p {
margin: 0;
}
}
.tab-content p {
margin: 1em;
}
/* :checked */
.tab input:checked ~ .tab-content {
padding: 16px 26px;
max-height: 100vh;
}
/* Icon */
.tab label::after {
position: absolute;
right: 0;
top: 0;
line-height: 3;
text-align: center;
-webkit-transition: all 0.35s;
-o-transition: all 0.35s;
transition: all 0.35s;
height: 100%;
margin-right: 12px;
display: flex;
align-items: center;
}
.tab input[type="checkbox"] + label::after {
content: "+";
}
.tab input[type="radio"] + label::after {
content: "\25BC";
}
.tab input[type="checkbox"]:checked + label::after {
transform: rotate(315deg);
}
.tab input[type="radio"]:checked + label::after {
transform: rotateX(180deg);
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AccordionComponent } from './accordion.component';
describe('AccordionComponent', () => {
let component: AccordionComponent;
let fixture: ComponentFixture<AccordionComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AccordionComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AccordionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,23 @@
import { Component, Input, OnInit } from '@angular/core';
import { v4 as uuidv4 } from 'uuid';
export interface IAccordionData {
header: string;
body: string;
}
@Component({
selector: 'app-accordion[header]',
templateUrl: './accordion.component.html',
styleUrls: ['./accordion.component.scss']
})
export class AccordionComponent implements OnInit {
@Input() header!: string
public guid: string = uuidv4()
constructor() { }
ngOnInit(): void {
}
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CardComponent } from './card.component'; import { CardComponent } from './card.component';
describe('CardComponent', () => { describe('CardComponent', () => {
let component: CardComponent; let component: CardComponent;
let fixture: ComponentFixture<CardComponent>; let fixture: ComponentFixture<CardComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ CardComponent ] declarations: [ CardComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(CardComponent); fixture = TestBed.createComponent(CardComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,15 +1,15 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
@Component({ @Component({
selector: 'app-card', selector: 'app-card',
templateUrl: './card.component.html', templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'] styleUrls: ['./card.component.scss']
}) })
export class CardComponent implements OnInit { export class CardComponent implements OnInit {
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
} }
} }

View File

@ -0,0 +1,7 @@
<span class="example-pizza-party" matSnackBarLabel>
Вы действительно хотите выйти?
</span>
<div class="buttons-container">
<button mat-button (click)="rejection()" style="background: red;">Нет</button>
<button mat-button (click)="logout()">Да</button>
</div>

View File

@ -0,0 +1,22 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
.buttons-container {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
}
button {
padding: 10px 62px;
margin-top: 8px;
background-color: var(--button-color);
color: var(--button-text-color);
border-radius: 3px;
border: none;
width: calc(50% - 12px);
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ExitComponent } from './exit.component'; import { ExitComponent } from './exit.component';
describe('ExitComponent', () => { describe('ExitComponent', () => {
let component: ExitComponent; let component: ExitComponent;
let fixture: ComponentFixture<ExitComponent>; let fixture: ComponentFixture<ExitComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ ExitComponent ] declarations: [ ExitComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(ExitComponent); fixture = TestBed.createComponent(ExitComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -0,0 +1,21 @@
import { Component } from '@angular/core';
import { MatBottomSheetRef } from '@angular/material/bottom-sheet';
@Component({
selector: 'app-exit',
templateUrl: './exit.component.html',
styleUrls: ['./exit.component.scss'],
})
export class ExitComponent {
constructor(
private _bottomSheetRef: MatBottomSheetRef<ExitComponent>
) {}
rejection() {
this._bottomSheetRef.dismiss(false)
}
logout() {
this._bottomSheetRef.dismiss(true)
}
}

View File

@ -1,18 +1,17 @@
<div class="footer-buttons-container"> <div class="footer-buttons-container">
<!-- *ngIf="deferredPrompt && token?.length" --> <!-- <button
<button *ngIf="((deviceType == 'android' && deferredPrompt) || deviceType == 'ios') && token?.length"
*ngIf="((deviceType == 'android' && deferredPrompt) || deviceType == 'ios') && token?.length" class="footer-buttons-container__button download"
class="footer-buttons-container__button download" (click)="downloadPWA()"
(click)="downloadPWA()" >
> <img src="./assets/download.svg" alt="download" />
<img src="./assets/download.svg" alt="download" /> </button> -->
</button> <button
<button *ngIf="!isPermissionNotifications && token?.length"
*ngIf="!isPermissionNotifications && token?.length" class="footer-buttons-container__button"
class="footer-buttons-container__button" (click)="requestPermission()"
(click)="requestPermission()" value="click to copy"
value="click to copy" >
> <img src="./assets/notification.svg" alt="notification" />
<img src="./assets/notification.svg" alt="notification" /> </button>
</button> </div>
</div>

View File

@ -1,39 +1,39 @@
:host { :host {
.footer-buttons-container { .footer-buttons-container {
position: fixed; position: fixed;
bottom: 16px; bottom: 16px;
right: 0; right: 0;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
z-index: 9999; z-index: 9999;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
padding: 0 8px; padding: 0 8px;
&__button { &__button {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 60px; width: 60px;
height: 60px; height: 60px;
background: #09467f; background: var(--text-color);
border: solid #fff 1px !important; border: solid var(--text-color) 1px !important;
color: #fff; color: var(--text-color);
border-radius: 100%; border-radius: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
margin-top: 8px; margin-top: 8px;
margin-right: 0; margin-right: 0;
cursor: pointer; cursor: pointer;
img { img {
width: 60%; width: 60%;
} }
} }
} }
@media (display-mode: standalone) { @media (display-mode: standalone) {
.download { .download {
display: none; display: none;
} }
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterButtonsComponent } from './footer-buttons.component'; import { FooterButtonsComponent } from './footer-buttons.component';
describe('FooterButtonsComponent', () => { describe('FooterButtonsComponent', () => {
let component: FooterButtonsComponent; let component: FooterButtonsComponent;
let fixture: ComponentFixture<FooterButtonsComponent>; let fixture: ComponentFixture<FooterButtonsComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ FooterButtonsComponent ] declarations: [ FooterButtonsComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(FooterButtonsComponent); fixture = TestBed.createComponent(FooterButtonsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -0,0 +1,24 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-footer-buttons',
templateUrl: './footer-buttons.component.html',
styleUrls: ['./footer-buttons.component.scss']
})
export class FooterButtonsComponent implements OnInit {
@Input() token!: string;
@Input() isPermissionNotifications!: boolean;
@Output() requestingPermission = new EventEmitter<null>();
public deviceType: 'ios' | 'android' | null = null;
constructor(
) { }
ngOnInit(): void {
}
requestPermission() {
this.requestingPermission.emit(null)
}
}

View File

@ -0,0 +1,4 @@
<img src="/assets/logo.svg" alt="Логотип">
<h3>Горячая линия</h3>
<a class="phone-number" href="tel:88003334130">8 (800) 333-41-30</a>
<app-social-media-buttons></app-social-media-buttons>

View File

@ -0,0 +1,28 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
padding-bottom: 100px;
h3 {
margin: 0;
font-family: Montserrat;
font-style: normal;
font-weight: 400;
font-size: 15px;
line-height: 20px;
text-align: center;
letter-spacing: -0.24px;
color: #6a737c;
}
.phone-number {
font-style: normal;
font-weight: 700;
font-size: 17px;
line-height: 22px;
text-align: center;
letter-spacing: -0.408px;
color: #d9d9d9;
text-decoration: none;
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FooterComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,25 @@
<h2>Пригласи друзей!</h2>
<div class="container">
<p>
Пригласи друзей зарегистрироваться в приложении по твоему уникальному коду и
получи бонусы, когда они совершат первую покупку.
</p>
<button class="share" (click)="share()" [disabled]="loading">
<ng-container *ngIf="loading">
<ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container>
</ng-container>
<mat-icon
*ngIf="!loading"
aria-hidden="false"
aria-label="Share"
fontIcon="share"
style="color: #fff"
></mat-icon>
</button>
</div>
<ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template>

View File

@ -0,0 +1,34 @@
:host {
padding: 16px;
h2 {
font-family: Montserrat;
font-style: normal;
font-weight: 700;
font-size: 17px;
line-height: 22px;
letter-spacing: -0.408px;
}
& .container {
width: 100%;
display: flex;
flex-direction: row;
gap: 16px;
p {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
color: var(--text-color);
}
.share {
width: 72px;
height: 48px;
background: var(--button-color);
padding: 8px 26px;
border: none;
}
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InviteFriendsComponent } from './invite-friends.component';
describe('InviteFriendsComponent', () => {
let component: InviteFriendsComponent;
let fixture: ComponentFixture<InviteFriendsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InviteFriendsComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(InviteFriendsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,54 @@
import { Component, OnInit } from '@angular/core';
import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-invite-friends',
templateUrl: './invite-friends.component.html',
styleUrls: ['./invite-friends.component.scss']
})
export class InviteFriendsComponent implements OnInit {
public refUrl: string = `${environment.defaultUrl}/?refUserId=`
public loading: boolean = true;
private shareData: ShareData = {
title: ''
}
constructor(
private jsonrpc: JsonrpcService,
private messageService: MessageService
) { }
async ngOnInit() {
const accountData = (await lastValueFrom(
this.jsonrpc
.rpc(
{
method: 'getTokenData',
params: [],
},
RpcService.authService,
true
)
)).data
this.refUrl += accountData.user_id
this.loading = false
}
share() {
if (navigator.share) {
navigator.share({
title: document.title,
text: "Tasty Coffee",
url: this.refUrl
})
.then(() => console.log('Successful share'))
.catch((error) => {
console.log('Error sharing:', error)
});
}
}
}

View File

@ -0,0 +1,31 @@
<h2>Ваш предыдущий заказ</h2>
<div class="info-order">
<p class="flex"><span>Дата: </span>
<span *ngIf="!loading">{{(lastOrder?.transactionCreateDate | date:'dd.MM.yyyyг.') || 'Данные не найдены'}}</span>
<ng-container *ngIf="loading">
<ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container>
</ng-container>
</p>
<p class="flex"><span>На сумму: </span>
<span *ngIf="!loading">{{lastOrder?.orderSum ? lastOrder?.orderSum + ' ₽' : 'Данные не найдены'}}</span>
<ng-container *ngIf="loading">
<ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 24 }"
></ng-container>
</ng-container>
</p>
</div>
<a href="https://yandex.ru/profile/151770398186" target="_blank">
<button class="evaluate-order">Оценить заказ</button>
</a>
<p class="info">
Списание бонусов возможно на любые категории. Бонусами можно оплатить 100%
суммы покупки. Бонусы начисляются только на напитки с учётом добавок.
Неиспользованные бонусы сгорают в течение 90 дней.
</p>
<ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template>

View File

@ -0,0 +1,52 @@
:host {
padding: 24px 16px 0px;
& > h2 {
font-family: Montserrat;
font-style: normal;
font-weight: 700;
font-size: 15px;
line-height: 20px;
letter-spacing: -0.24px;
}
& > .info-order {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
span {
color: #828282;
}
.flex {
display: flex;
align-items: center;
flex-direction: row;
gap: 8px;
}
}
.evaluate-order {
margin: 24px 0;
width: 100%;
padding: 12px;
text-align: center;
border: 2px solid #28af49;
border-radius: 6px;
font-style: normal;
font-weight: 700;
font-size: 17px;
line-height: 22px;
letter-spacing: -0.408px;
background-color: transparent;
color: #28af49;
}
.info {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
color: rgba(255, 255, 255, 0.5);
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LastOrderComponent } from './last-order.component';
describe('LastOrderComponent', () => {
let component: LastOrderComponent;
let fixture: ComponentFixture<LastOrderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LastOrderComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LastOrderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,18 @@
import { Component, Input, OnInit } from '@angular/core';
import { Purchase } from 'src/app/interface/data';
@Component({
selector: 'app-last-order[lastOrder]',
templateUrl: './last-order.component.html',
styleUrls: ['./last-order.component.scss']
})
export class LastOrderComponent implements OnInit {
@Input() lastOrder!: Purchase;
@Input() loading!: boolean;
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,5 @@
<div class="container">
<mat-icon style="cursor: pointer;" aria-hidden="false" aria-label="Назад" fontIcon="arrow_back_ios" class="back-arrow" (click)="back()"></mat-icon>
<h1 class="title">{{title}}</h1>
<div class="plug"></div>
</div>

View File

@ -0,0 +1,33 @@
:host {
width: 100%;
}
.container {
box-sizing: border-box;
padding: 12px 16px;
width: 100%;
background: #231f20;
box-shadow: 0px 8px 16px rgba(17, 17, 17, 0.5);
color: var(--text-color);
display: flex;
align-items: center;
justify-content: space-between;
.back-arrow {
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.plug {
height: 24px;
width: 24px;
visibility: hidden;
}
.title {
font-family: "Montserrat", sans-serif;
font-weight: 700;
font-size: 17px;
line-height: 22px;
margin: 0;
}
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NavbarComponent } from './navbar.component'; import { NavbarComponent } from './navbar.component';
describe('NavbarComponent', () => { describe('NavbarComponent', () => {
let component: NavbarComponent; let component: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>; let fixture: ComponentFixture<NavbarComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NavbarComponent ] declarations: [ NavbarComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(NavbarComponent); fixture = TestBed.createComponent(NavbarComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -0,0 +1,21 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-navbar[title]',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
@Input() title: string = 'Название не задано'
@Output() backEvent = new EventEmitter<null>();
constructor() { }
ngOnInit(): void {
}
back() {
this.backEvent.emit(null)
}
}

View File

@ -1,98 +1,97 @@
.woocommerce-MyAccount-content { .woocommerce-MyAccount-content {
max-width: 400px; max-width: 400px;
margin-left: calc(50vw - 200px); margin-left: calc(50vw - 200px);
} }
.container-progressbar { .container-progressbar {
width: 80%; width: 80%;
transform: translate(0, 0); transform: translate(0, 0);
margin: 18px 10% 90px; margin: 18px 10% 90px;
} }
.steps { .steps {
position: relative; position: relative;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
} }
.step { .step {
width: 20px; width: 20px;
height: 20px; height: 20px;
background: #fff; border: 2px solid #acaca6;
border: 2px solid #acaca6; border-radius: 50%;
border-radius: 50%; transition: background 1s;
transition: background 1s; position: relative;
position: relative; .status-image {
.status-image { width: 80px;
width: 80px; height: 80px;
height: 80px; position: absolute;
position: absolute; top: 25px;
top: 25px; left: -32px;
left: -32px; }
} }
} .step.selected {
.step.selected { border: 2px solid #f9b004;
border: 2px solid #f9b004; }
} .step.completed {
.step.completed { border: 2px solid #f9b004;
border: 2px solid #f9b004; background: #f9b004;
background: #f9b004; }
} .step.cancelled {
.step.cancelled { background: #d7120b;
background: #d7120b; border: 2px solid #d7120b;
border: 2px solid #d7120b; }
} .progress {
.progress { position: absolute;
position: absolute; width: 100%;
width: 100%; height: 50%;
height: 50%; border-bottom: 2px solid #acaca6;
border-bottom: 2px solid #acaca6; z-index: -1;
z-index: -1; }
} .percent {
.percent { position: absolute;
position: absolute; width: 0;
width: 0; top: 2px;
top: 2px; height: 100%;
height: 100%; border-bottom: 2px solid #f9b004;
border-bottom: 2px solid #f9b004; z-index: 1;
z-index: 1; transition: width 1s;
transition: width 1s; }
} p {
p { margin: 16px 0;
margin: 16px 0; }
} .woocommerce-order-details {
.woocommerce-order-details { &__title {
&__title { font-weight: 600;
font-weight: 600; font-size: 18px;
font-size: 18px; }
} }
} .woocommerce-table {
.woocommerce-table { border: 2px solid #dee2e6;
border: 2px solid #dee2e6; border-radius: 0.25rem;
border-radius: 0.25rem; border-collapse: separate;
border-collapse: separate; text-align: left;
text-align: left; margin-top: 8px;
margin-top: 8px; th {
th { font-weight: 600;
font-weight: 600; }
} th, td {
th, td { padding: 0.7rem 1.5rem;
padding: 0.7rem 1.5rem; border-bottom: 2px solid #dee2e6;
border-bottom: 2px solid #dee2e6; }
} }
} .woocommerce-column {
.woocommerce-column { &__title {
&__title { margin-top: 16px;
margin-top: 16px; font-weight: 600;
font-weight: 600; font-size: 18px;
font-size: 18px; }
} }
} address {
address { border: 1px solid #dee2e6;
border: 1px solid #dee2e6; border-bottom-width: 2px;
border-bottom-width: 2px; border-right-width: 2px;
border-right-width: 2px; text-align: left;
text-align: left; width: 100%;
width: 100%; border-radius: 5px;
border-radius: 5px; padding: 10px 12px;
padding: 10px 12px; margin-top: 8px;
margin-top: 8px; }
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderInfoComponent } from './order-info.component'; import { OrderInfoComponent } from './order-info.component';
describe('OrderInfoComponent', () => { describe('OrderInfoComponent', () => {
let component: OrderInfoComponent; let component: OrderInfoComponent;
let fixture: ComponentFixture<OrderInfoComponent>; let fixture: ComponentFixture<OrderInfoComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ OrderInfoComponent ] declarations: [ OrderInfoComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(OrderInfoComponent); fixture = TestBed.createComponent(OrderInfoComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,43 +1,43 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { orderStatuses } from 'src/app/app.constants'; import { orderStatuses } from 'src/app/app.constants';
import { AcceptedOrder, OrderProduct } from 'src/app/interface/data'; import { AcceptedOrder, OrderProduct } from 'src/app/interface/data';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
@Component({ @Component({
selector: 'app-order-info', selector: 'app-order-info',
templateUrl: './order-info.component.html', templateUrl: './order-info.component.html',
styleUrls: ['./order-info.component.scss'] styleUrls: ['./order-info.component.scss']
}) })
export class OrderInfoComponent implements OnInit { export class OrderInfoComponent implements OnInit {
@Input() order!: AcceptedOrder; @Input() order!: AcceptedOrder;
@Input() loadingStatus: boolean = false; @Input() loadingStatus: boolean = false;
percent: number = 0; percent: number = 0;
readonly moment = moment; readonly moment = moment;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
this.setPercent(); this.setPercent();
} }
setPercent() { setPercent() {
const statusIndex = this.getStatusIndex(this.order.status); const statusIndex = this.getStatusIndex(this.order.status);
if (statusIndex !== -1 && statusIndex !== 0) { if (statusIndex !== -1 && statusIndex !== 0) {
this.percent = (statusIndex - 1) * 20; this.percent = (statusIndex - 1) * 20;
} }
} }
formatStatus(status: string): string|undefined{ formatStatus(status: string): string|undefined{
const key = Object.keys(orderStatuses).find((el) => status === el) ?? ''; const key = Object.keys(orderStatuses).find((el) => status === el) ?? '';
return orderStatuses[key]; return orderStatuses[key];
} }
getStatusIndex(status: string) { getStatusIndex(status: string) {
return [...new Set(Object.values(orderStatuses))].findIndex((value) => value === this.formatStatus(status)); return [...new Set(Object.values(orderStatuses))].findIndex((value) => value === this.formatStatus(status));
} }
getItemModifiersName(item: OrderProduct): string[]{ getItemModifiersName(item: OrderProduct): string[]{
return Object.keys(item.modifiers); return Object.keys(item.modifiers);
} }
} }

View File

@ -0,0 +1,3 @@
<a *ngFor="let link of links" [href]="link.url" target="_blank">
<img [src]="link.imgUrl" [alt]="link.label" />
</a>

View File

@ -0,0 +1,15 @@
:host {
display: flex;
flex-direction: row;
gap: 16px;
justify-content: space-between;
a {
width: 48px;
height: 48px;
border-radius: 100%;
background: #333333;
display: flex;
align-items: center;
justify-content: center;
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SocialMediaButtonsComponent } from './social-media-buttons.component';
describe('SocialMediaButtonsComponent', () => {
let component: SocialMediaButtonsComponent;
let fixture: ComponentFixture<SocialMediaButtonsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SocialMediaButtonsComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(SocialMediaButtonsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,38 @@
import { Component, OnInit } from '@angular/core';
export interface ISocialMediaLink {
url: string;
imgUrl: string;
label: string;
}
@Component({
selector: 'app-social-media-buttons',
templateUrl: './social-media-buttons.component.html',
styleUrls: ['./social-media-buttons.component.scss']
})
export class SocialMediaButtonsComponent implements OnInit {
public links: ISocialMediaLink[] = [
{
label: 'Инстаграм',
url: 'https://www.instagram.com/',
imgUrl: '/assets/social-media-icons/instagram.svg'
},
{
label: 'ВК',
url: 'https://vk.com/coffeelike_com',
imgUrl: '/assets/social-media-icons/vk.svg'
},
{
label: 'Youtube',
url: 'https://www.youtube.com/c/coffeelikeru',
imgUrl: '/assets/social-media-icons/youtube.svg'
}
]
constructor() { }
ngOnInit(): void {
}
}

View File

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FocusNextInputDirective } from './focus-next-input.directive';
import { DownloadAppDirective } from './download-app.directive';
@NgModule({
imports: [
CommonModule
],
declarations: [FocusNextInputDirective, DownloadAppDirective],
exports: [FocusNextInputDirective, DownloadAppDirective]
})
export class DirectivesModule { }

View File

@ -0,0 +1,81 @@
import {
Directive,
ElementRef,
HostListener,
OnInit,
Renderer2,
} from '@angular/core';
import { MessageService } from 'primeng/api';
import { getTypeDevice, pwaInstalled } from 'src/app/utils';
@Directive({
selector: '[appDownloadApp]',
})
export class DownloadAppDirective implements OnInit {
public deviceType: 'ios' | 'android' | null = null;
public deferredPrompt: any;
constructor(
private messageService: MessageService,
public renderer: Renderer2,
private el: ElementRef,
) { }
ngOnInit(): void {
getTypeDevice();
if (this.deviceType === 'ios') {
this.el.nativeElement.style.display = 'block';
}
this.checkInstalled();
}
async checkInstalled(): Promise<void> {
if (window.matchMedia('(display-mode: standalone)').matches
|| await pwaInstalled()) {
this.el.nativeElement.style.display = 'none';
}
}
@HostListener('window:beforeinstallprompt', ['$event'])
onBeforeInstallPrompt(e: any) {
e.preventDefault();
this.deferredPrompt = e;
if (this.deferredPrompt) {
this.el.nativeElement.style.display = 'block';
}
}
@HostListener('window:appinstalled', ['$event'])
onAppInstalled(e: any) {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
this.deferredPrompt = e;
this.el.nativeElement.style.display = 'none';
}
@HostListener('click', ['$event'])
async downloadApp(event: MouseEvent) {
if (!this.deferredPrompt) {
this.messageService.clear();
this.messageService.add({
severity: 'error',
summary: 'Не поддерживается в Вашем браузере!',
});
return;
}
this.deferredPrompt.prompt();
this.deferredPrompt.userChoice.then((res: any) => {
if (res.outcome === 'accepted') {
this.messageService.clear();
this.messageService.add({
severity: 'success',
summary: 'Спасибо за установку!',
});
}
this.deferredPrompt = null;
});
}
}

View File

@ -0,0 +1,29 @@
import { Directive, EventEmitter, Input, Renderer2 } from '@angular/core';
@Directive({
selector: '[appFocusNextInput]'
})
export class FocusNextInputDirective {
@Input('appFocusNextInput') eventEmitter!: EventEmitter<string>;
constructor(private renderer: Renderer2) { }
ngOnInit() {
// TODO:
// 1: don't need to listen all events
// 2: not working in safari
this.eventEmitter.subscribe(elementId => {
try {
const element = this.renderer.selectRootElement(elementId)
setTimeout(() => {
element.focus();
element.click();
}, 0);
} catch (ex) {
(document.activeElement as HTMLElement).blur();
// If the element doesn't exist or if the element disappears when this called then no need to do anything
}
});
}
}

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { AuthGuard } from './auth-guard.guard';
describe('AuthGuardGuard', () => {
let guard: AuthGuard;
beforeEach(() => {
TestBed.configureTestingModule({});
guard = TestBed.inject(AuthGuard);
});
it('should be created', () => {
expect(guard).toBeTruthy();
});
});

View File

@ -0,0 +1,47 @@
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
CanActivateChild,
Router,
RouterStateSnapshot,
UrlTree,
} from '@angular/router';
import { Observable, of } from 'rxjs';
import { CookiesService } from '../services/cookies.service';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private cookiesService: CookiesService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>
| boolean
| UrlTree
{
if (this.cookiesService.getItem('token')) {
return of(true);
} else {
this.router.navigate(['/login']);
return of(false);
}
}
canActivateChild(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| boolean
| UrlTree
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree>
{
return this.canActivate(childRoute, state);
}
}

View File

@ -0,0 +1,222 @@
export enum PageCode {
Auth,
Orders,
BonusProgram,
RefSystem,
UserData,
}
export interface Moment extends moment.Moment { }
export interface Page {
code: PageCode;
component?: any;
name: string;
description?: string;
getMethod?: string;
resName?: string;
onSideBar: boolean;
}
export interface UserDataForm {
first_name: string;
birthdate: string;
gender: string;
}
export interface BonusProgramAccount {
BonusProgramName: string;
BonusProgramTypeID: string;
CardNumber: number;
Bonuses: number;
HoldedBonuses: number;
BonusProgramAccounts: BonusProgramAccount[];
DateBonusBurn: string;
_links: any[];
_embedded: any;
}
export interface Purchase {
PurchaseId?: string;
CustomerId?: string;
PurchaseDate: string;
PurchaseState?: number;
CardNumber?: number;
Address?: string;
CheckSummary?: number;
BonusSummary?: number;
ID: string;
Transactions: Transaction[];
IsSingleTransaction?: boolean;
transactionCreateDate?: string;
transactionType?: 'CancelPayFromWallet' | 'PayFromWallet' | 'RefillWallet';
transactionSum: number;
orderSum?: number;
}
export interface Transaction {
User: string;
Purchase: string;
Date: string;
Value: number;
TransactionType: number;
UserBonusesSnapshot: number;
BonusPercent: number;
DateActiveBonus: string;
AccountBonus: string;
Bonus: string;
ID: string;
HasPurchase?: boolean;
}
export interface OrderStatus {
[key: string]: string;
}
export interface lvlPeriod {
percent: number;
start: number;
end?: number;
color: string;
}
export interface DeliveryType {
cost: number;
title: string;
id: number;
}
export interface AcceptedOrder {
id: number;
status: string;
currency_symbol: string;
total: number;
address: {
city: string;
street: string;
house: number;
flat: number;
};
payment_method: string;
shipping: {
name: string;
total: number;
};
date_created: string;
items: OrderProduct[];
}
export interface Product {
id: number;
name: string;
price: string;
image_url: string;
image_gallery: string[];
category_id: number;
description?: string;
stock_status: string;
currency_symbol: string;
modifier_data: Modifier[];
short_description?: string;
guid?: string;
}
export interface Modifier {
id: number;
name: string;
category_type: string;
minimum_options: number;
maximum_options: number;
global_categories: string;
required: number;
options: Option[];
allOptions?: Option[];
}
export interface Option {
id: number;
name: string;
price: string;
prechecked: string;
active?: boolean;
}
export interface OrderProduct {
id: number;
amount: number;
name: string;
price: number;
modifiers: {
[name: string]: OrderModifier[];
};
}
export interface OrderModifier {
name: string;
id: number;
price: number;
}
export interface DeliveryData {
paymentMethod: PaymentMethod;
deliveryDate: Date | null;
deliveryType: DeliveryType | null;
persons: number;
comment: string;
}
export interface PaymentMethod {
type: string;
label: string;
}
export interface UserData {
first_name: string | null;
last_name: string | null;
street: string | null;
house: string | null;
flat: string | null;
city: string;
phone: string | null;
}
export interface UserInfoCategory {
id: string;
isActive: boolean;
isDefaultForNewGuests: boolean;
name: string;
}
export interface UserInfoWallet {
id: string;
name: string;
programType: string;
type: string;
}
export interface UserInfoWalletBalance {
balance: number;
wallet: UserInfoWallet;
}
export interface UserInfo {
OrdersSum: number;
categories: UserInfoCategory[];
customer_level: number;
birthday: string;
id: string;
name: string | null;
phone: string;
walletBalances: UserInfoWalletBalance[] | { error: ResponseError };
}
export interface ResponseError {
code: number;
msg: string;
}
export interface UpdateNewCustomerRequest {
name: string;
sex: string;
birthday: string;
}

View File

@ -1,62 +1,62 @@
import {Modifier, Product} from "../interface/data"; import {Modifier, Product} from "../interface/data";
export class OrderProduct implements Product{ export class OrderProduct implements Product{
constructor(product: Product,guid: string, amount: number = 1) { constructor(product: Product,guid: string, amount: number = 1) {
this.category_id = product.category_id; this.category_id = product.category_id;
this.currency_symbol = product.currency_symbol; this.currency_symbol = product.currency_symbol;
this.description = product.description; this.description = product.description;
this.id = product.id; this.id = product.id;
this.image_gallery = product.image_gallery; this.image_gallery = product.image_gallery;
this.image_url = product.image_url; this.image_url = product.image_url;
this.modifier_data = product.modifier_data; this.modifier_data = product.modifier_data;
this.name = product.name; this.name = product.name;
this.price = product.price; this.price = product.price;
this.stock_status = product.stock_status; this.stock_status = product.stock_status;
this.amount = amount; this.amount = amount;
this.guid = guid; this.guid = guid;
this.short_description = product.short_description; this.short_description = product.short_description;
} }
public amount: number; public amount: number;
public category_id: number; public category_id: number;
public currency_symbol: string; public currency_symbol: string;
public description?: string; public description?: string;
public short_description?: string; public short_description?: string;
public id: number; public id: number;
public image_gallery: string[]; public image_gallery: string[];
public image_url: string; public image_url: string;
public modifier_data: Modifier[]; public modifier_data: Modifier[];
public name: string; public name: string;
public price: string; public price: string;
public stock_status: string; public stock_status: string;
public guid: string; public guid: string;
get finalPrice(): number{ get finalPrice(): number{
const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => { const modifiersPrice = this.modifier_data.reduce<number>((previousValue, currentValue) => {
return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => { return previousValue + currentValue.options.reduce<number>((previousOptionValue, currentOptionValue) => {
return previousOptionValue + Number(currentOptionValue.price); return previousOptionValue + Number(currentOptionValue.price);
}, 0); }, 0);
}, 0); }, 0);
return (Number(this.price) + modifiersPrice) * this.amount; return (Number(this.price) + modifiersPrice) * this.amount;
new Date() new Date()
} }
toJson(){ toJson(){
return { return {
id: this.id, id: this.id,
amount: this.amount, amount: this.amount,
name: this.name, name: this.name,
modifiers: this.modifier_data?.map(modifier => { modifiers: this.modifier_data?.map(modifier => {
return { return {
id: modifier.id, id: modifier.id,
options: modifier.options, options: modifier.options,
} }
}), }),
} }
} }
} }

View File

@ -1,68 +1,68 @@
import {DeliveryData, UserData} from "../interface/data"; import {DeliveryData, UserData} from "../interface/data";
import {OrderProduct} from "./order-product"; import {OrderProduct} from "./order-product";
import * as moment from 'moment'; import * as moment from 'moment';
import { CookiesService } from "../services/cookies.service"; import { CookiesService } from "../services/cookies.service";
export interface OrderInfo { export interface OrderInfo {
products: OrderProduct[]; products: OrderProduct[];
userData?: UserData; userData?: UserData;
deliveryData?: DeliveryData; deliveryData?: DeliveryData;
phone: string; phone: string;
token: string | undefined; token: string | undefined;
} }
export class Order { export class Order {
public products: OrderProduct[]; public products: OrderProduct[];
public userData?: UserData; public userData?: UserData;
public deliveryData?: DeliveryData; public deliveryData?: DeliveryData;
public phone: string; public phone: string;
public token: string | undefined; public token: string | undefined;
constructor( constructor(
orderInfo: OrderInfo orderInfo: OrderInfo
) { ) {
this.products = orderInfo.products; this.products = orderInfo.products;
this.userData = orderInfo.userData; this.userData = orderInfo.userData;
this.deliveryData = orderInfo.deliveryData; this.deliveryData = orderInfo.deliveryData;
this.phone = orderInfo.phone; this.phone = orderInfo.phone;
this.token = orderInfo.token; this.token = orderInfo.token;
} }
get price(): number { get price(): number {
return this.products.reduce<number>((previousValue, currentValue) => previousValue + currentValue.finalPrice, 0); return this.products.reduce<number>((previousValue, currentValue) => previousValue + currentValue.finalPrice, 0);
} }
toJson(): any { toJson(): any {
const date = moment(this.deliveryData?.deliveryDate ?? Date.now()); const date = moment(this.deliveryData?.deliveryDate ?? Date.now());
return { return {
items: this.products.map(product => { items: this.products.map(product => {
return product.toJson(); return product.toJson();
}), }),
user_data: { user_data: {
phone: this.phone, phone: this.phone,
...this.userData ...this.userData
}, },
payment_method: this.deliveryData?.paymentMethod.type, payment_method: this.deliveryData?.paymentMethod.type,
delivery_time: date.format('HH:mm'), delivery_time: date.format('HH:mm'),
delivery_date: date.format("YYYY-MM-DD"), delivery_date: date.format("YYYY-MM-DD"),
delivery_instance_id: this.deliveryData?.deliveryType?.id, delivery_instance_id: this.deliveryData?.deliveryType?.id,
persons: 1, persons: 1,
payments: [ payments: [
{ {
type: this.deliveryData?.paymentMethod.type, type: this.deliveryData?.paymentMethod.type,
summ: this.price, summ: this.price,
}, },
{ {
type: "crm4retail", type: "crm4retail",
summ: 0, summ: 0,
payload: { payload: {
id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d" id: "c07a10d8-ba7e-43b0-92aa-ae470060bc7d"
} }
} }
], ],
comment: this.deliveryData?.comment, comment: this.deliveryData?.comment,
token: this.token token: this.token
} }
} }
} }

View File

@ -1,75 +1,96 @@
<div class="woocommerce"> <div
<div [ngClass]="{
*ngIf="currentPage.code !== PageCode.Auth" woocommerce: true,
class="top-left-attribute" 'auth-page': currentPage.code === PageCode.Auth
></div> }"
<div [ngSwitch]="currentPage.code" class=""> >
<ng-container *ngSwitchCase="PageCode.Auth"> <!-- <div
<app-auth *ngIf="currentPage.code !== PageCode.Auth"
[handleHttpError]="handleHttpErrorFunc" class="top-left-attribute"
(phoneConfirmed)="phoneConfirmed()" ></div>
></app-auth> <div [ngSwitch]="currentPage.code" class="">
</ng-container> <ng-container *ngSwitchCase="PageCode.Auth">
<ng-container *ngSwitchCase="PageCode.Orders"> <app-auth
<app-orders></app-orders> [handleHttpError]="handleHttpErrorFunc"
</ng-container> (phoneConfirmed)="phoneConfirmed()"
<ng-container *ngSwitchCase="PageCode.BonusProgram"> ></app-auth>
<app-bonus-program></app-bonus-program> </ng-container>
</ng-container> <ng-container *ngSwitchCase="PageCode.Orders">
<ng-container *ngSwitchCase="PageCode.UserData"> <app-orders></app-orders>
<app-user-data></app-user-data> </ng-container>
</ng-container> <ng-container *ngSwitchCase="PageCode.BonusProgram">
<ng-container *ngSwitchCase="PageCode.RefSystem"> <app-bonus-program></app-bonus-program>
<app-ref-system></app-ref-system> </ng-container>
</ng-container> <ng-container *ngSwitchCase="PageCode.UserData">
</div> <app-user-data></app-user-data>
<nav </ng-container>
*ngIf="currentPage.code !== PageCode.Auth" <ng-container *ngSwitchCase="PageCode.RefSystem">
class="woocommerce-MyAccount-navigation" <app-ref-system></app-ref-system>
> </ng-container>
<ul> </div>
<ng-container *ngFor="let page of pageList; let index = index"> <nav
<li *ngIf="currentPage.code !== PageCode.Auth"
*ngIf="page.onSideBar" class="woocommerce-MyAccount-navigation"
class="woocommerce-MyAccount-navigation-link" >
[ngClass]="{ <ul>
'is-active': page === currentPage, <ng-container *ngFor="let page of pageList; let index = index">
first: index === 1 <li
}" *ngIf="page.onSideBar"
(click)="changePage($event, page)" class="woocommerce-MyAccount-navigation-link"
> [ngClass]="{
<div class="container"> 'is-active': page === currentPage,
<img first: index === 1
src="{{ './assets/menu-icons/' + page.resName + '.png' }}" }"
alt="Иконка меню" (click)="changePage($event, page)"
/> >
<div class="menu-item-info"> <div class="container">
<a href="#">{{ page.name }}</a> <img
<p>{{ page.description }}</p> style="width: 36px"
</div> src="{{ './assets/menu-icons/' + page.resName + '.png' }}"
</div> alt="Иконка меню"
</li> />
</ng-container> <div class="menu-item-info">
<li <a href="#">{{ page.name }}</a>
class="woocommerce-MyAccount-navigation-link" <p>{{ page.description }}</p>
(click)="logout($event)" </div>
> </div>
<div class="container"> </li>
<img src="./assets/menu-icons/exit.png" alt="Иконка меню" /> </ng-container>
<div class="menu-item-info"> <li
<a href="#">Выход</a> class="woocommerce-MyAccount-navigation-link download-app"
</div> appDownloadApp
</div> >
</li> <div class="container">
</ul> <img
</nav> style="width: 36px"
<span src="./assets/menu-icons/download-app.png"
class="version" alt="Иконка меню"
[ngClass]="{ />
version: true, <div class="menu-item-info">
bottom: currentPage.code === PageCode.Auth <a href="#">Установить приложение</a>
}" <p>{{ page.description }}</p>
> </div>
v{{ version }} </div>
</span> </li>
</div> <li
class="woocommerce-MyAccount-navigation-link"
(click)="logout($event)"
>
<div class="container">
<img src="./assets/menu-icons/exit.png" alt="Иконка меню" />
<div class="menu-item-info">
<a href="#">Выход</a>
</div>
</div>
</li>
</ul>
</nav> -->
<span
class="version"
[ngClass]="{
version: true
}"
>
v{{ version }}
</span>
</div>

View File

@ -1,82 +1,100 @@
:host { :host {
.woocommerce { .woocommerce {
min-height: calc(100vh - 39px); min-height: calc(100vh - 39px);
padding: 20px 18px; padding: 20px 18px;
nav {
margin-bottom: 24px; &.auth-page {
margin-top: 26px; display: flex;
display: flex; flex-direction: column;
justify-content: center; justify-content: space-between;
ul { }
max-width: 400px;
width: 100%; nav {
border-radius: 6px; margin-bottom: 24px;
display: flex; margin-top: 26px;
justify-content: space-between; display: flex;
flex-direction: column; justify-content: center;
li {
padding: 12px; ul {
width: 100%; max-width: 400px;
text-align: center; width: 100%;
cursor: pointer; border-radius: 6px;
height: 81px; display: flex;
margin-bottom: 10px; justify-content: space-between;
background: #ffffff; flex-direction: column;
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
color: #000; li {
border-radius: 15px; padding: 12px;
&.is-active { width: 100%;
border: solid #09467f 1px; text-align: center;
// display: none; cursor: pointer;
} height: 81px;
&.first { margin-bottom: 10px;
// border-radius: 7px 0 0 7px; background: #ffffff;
} box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
.container { color: #000;
display: flex; border-radius: 15px;
align-items: center;
height: 100%; &.is-active {
.menu-item-info { border: solid var(--main-color) 1px;
margin-left: 16px; // display: none;
& > a { }
font-style: normal;
font-weight: 700; &.first {
font-size: 18px; // border-radius: 7px 0 0 7px;
line-height: 22px; }
text-decoration: none;
color: #000; &.download-app {
display: block; display: none;
text-align-last: left; }
}
& > p { .container {
font-style: normal; display: flex;
font-weight: 400; align-items: center;
font-size: 12px; height: 100%;
line-height: 15px;
text-align: left; .menu-item-info {
} margin-left: 16px;
}
} &>a {
} font-style: normal;
li:nth-child(odd) { font-weight: 700;
background-color: #ebebeb; font-size: 18px;
} line-height: 22px;
} text-decoration: none;
} color: #000;
.top-left-attribute { display: block;
width: 10px; text-align-last: left;
height: 5px; }
background: #09467f;
left: 0; &>p {
position: absolute; font-style: normal;
top: 69px; font-weight: 400;
} font-size: 12px;
.version { line-height: 15px;
opacity: 0.5; text-align: left;
&.bottom { }
position: absolute; }
bottom: 8px; }
} }
}
} li:nth-child(odd) {
} background-color: #ebebeb;
}
}
}
.top-left-attribute {
width: 10px;
height: 5px;
background: var(--main-color);
left: 0;
position: absolute;
top: 103px;
}
.version {
opacity: 0.5;
}
}
}

View File

@ -1,186 +1,205 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { CookiesService } from '../../services/cookies.service'; import { CookiesService } from '../../services/cookies.service';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Page, PageCode } from '../../interface/data'; import { Page, PageCode } from '../../interface/data';
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { PageList, PageListWithBonus } from '../../app.constants'; import { PageList, PageListWithBonus } from '../../app.constants';
import { HttpErrorResponse, HttpParams } from '@angular/common/http'; import { HttpErrorResponse, HttpParams } from '@angular/common/http';
import { ExitComponent } from '../../components/exit/exit.component'; import { ExitComponent } from '../../components/exit/exit.component';
import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs';
@Component({
selector: 'app-account', @Component({
templateUrl: './account.component.html', selector: 'app-account',
styleUrls: ['./account.component.scss'], templateUrl: './account.component.html',
providers: [DialogService], styleUrls: ['./account.component.scss'],
}) providers: [DialogService],
export class AccountComponent implements OnInit { })
@Output() setUserDataOrderPage = new EventEmitter<null>(); export class AccountComponent implements OnInit {
public refSystemId: string = ''; @Output() setUserDataOrderPage = new EventEmitter<null>();
public refSystemId: string = '';
constructor(
private cookiesService: CookiesService, constructor(
private router: Router, private cookiesService: CookiesService,
private route: ActivatedRoute, private router: Router,
private dialogService: DialogService, private route: ActivatedRoute,
private jsonRpcService: JsonrpcService, private dialogService: DialogService,
private messageService: MessageService private jsonRpcService: JsonrpcService,
) {} private messageService: MessageService
) {}
public currentPage!: Page;
public handleHttpErrorFunc = this.handleHttpError.bind(this); public currentPage!: Page;
private ref!: DynamicDialogRef; public handleHttpErrorFunc = this.handleHttpError.bind(this);
public version: string = environment.version; private ref!: DynamicDialogRef;
public version: string = environment.version;
readonly PageCode = PageCode;
readonly pageList = environment.hasBonusProgram readonly PageCode = PageCode;
? PageListWithBonus readonly pageList = environment.hasBonusProgram
: PageList; ? PageListWithBonus
: PageList;
ngOnInit(): void {
if (!this.getToken()) { ngOnInit(): void {
this.currentPage = this.pageList[0]; if (!this.getToken()) {
} else { this.currentPage = this.pageList[0];
this.route.queryParams.subscribe((params) => { } else {
if (!params['activePage']) { this.route.queryParams.subscribe((params) => {
this.currentPage = this.pageList[1]; if (!params['activePage']) {
return; this.currentPage = this.pageList[1];
} return;
const currentPage = this.pageList.find((page) => { }
return page.code === Number(params['activePage']); const currentPage = this.pageList.find((page) => {
}); return page.code === Number(params['activePage']);
if (!currentPage) { });
this.currentPage = this.pageList[1]; if (!currentPage) {
} else { this.currentPage = this.pageList[1];
this.currentPage = currentPage; } else {
} this.currentPage = currentPage;
}); }
} });
document.body.classList.add( }
'woocommerce-account', document.body.classList.add(
'woocommerce-page', 'woocommerce-account',
'woocommerce-orders' 'woocommerce-page',
); 'woocommerce-orders'
} );
}
phoneConfirmed(): void {
this.refSystem(); phoneConfirmed(): void {
this.currentPage = this.pageList[1]; this.refSystem();
} this.currentPage = this.pageList[1];
}
refSystem() {
this.route.queryParams.subscribe((params) => { async refSystem() {
if (params['refCardNumber']) { const additionalInfo = (await lastValueFrom(
this.refSystemId = params['refCardNumber']; this.jsonRpcService.rpc({
this.jsonRpcService method: 'getAdditionalInfo',
.rpc( params: []
{ }, RpcService.authService, true)
method: 'updateAdditionalInfo', )).data
params: [ this.route.queryParams.subscribe((params) => {
{ if (params['refUserId']) {
refSystem: { if (additionalInfo.refSystem?.code.length) {
code: this.refSystemId, this.messageService.add({
}, severity: 'custom',
}, summary:
], 'Вы уже зарегестрированы в реферальной программе!',
}, });
RpcService.authService, return;
true }
) this.refSystemId = params['refUserId'];
.subscribe({ this.jsonRpcService
next: () => { .rpc(
this.router.navigate([], { {
queryParams: { method: 'updateAdditionalInfo',
refCardNumber: null, params: [
}, {
queryParamsHandling: 'merge', refSystem: {
}); code: this.refSystemId,
this.messageService.add({ },
severity: 'custom', },
summary: ],
'Регистрация прошла успешна! Бонусы скоро будут начислены', },
}); RpcService.authService,
}, true
error: (err) => { )
console.error('Error: ', err); .subscribe({
this.messageService.add({ next: () => {
severity: 'error', this.router.navigate([], {
summary: 'Произошла ошибка, попробуйте позже', queryParams: {
}); refUserId: null,
}, },
}); queryParamsHandling: 'merge',
} });
}); this.messageService.add({
} severity: 'custom',
summary:
changePage(event: MouseEvent, page: Page): void { 'Регистрация прошла успешна! Бонусы скоро будут начислены',
event.preventDefault(); });
this.currentPage = page; },
// let params = new HttpParams(); error: (err) => {
// params = params.append('activePage', this.currentPage.code); console.error('Error: ', err);
this.router.navigate(['.'], { this.messageService.add({
relativeTo: this.route, severity: 'error',
queryParams: { summary: 'Произошла ошибка, попробуйте позже',
activePage: this.currentPage.code, });
}, },
queryParamsHandling: 'merge', });
// preserve the existing query params in the route }
// skipLocationChange: true });
// do not trigger navigation }
});
} changePage(event: MouseEvent, page: Page): void {
event.preventDefault();
handleHttpError(error: HttpErrorResponse): void { if (page.resName === 'download-app') {
if (error.status === 500) {
this.logout(); return
} }
} this.currentPage = page;
// let params = new HttpParams();
setToken(token: string): void { // params = params.append('activePage', this.currentPage.code);
this.cookiesService.setCookie('token', token); this.router.navigate(['.'], {
} relativeTo: this.route,
queryParams: {
getToken(): string | void { activePage: this.currentPage.code,
return this.cookiesService.getItem('token'); },
} queryParamsHandling: 'merge',
// preserve the existing query params in the route
deleteToken(): void { // skipLocationChange: true
this.cookiesService.deleteCookie('token'); // do not trigger navigation
// this.router.navigate(['.'], { });
// queryParams: {}, }
// });
} handleHttpError(error: HttpErrorResponse): void {
if (error.status === 500) {
logout(event?: MouseEvent) { this.logout();
if (event) { }
event.preventDefault(); }
}
this.ref = this.dialogService.open(ExitComponent, { setToken(token: string): void {
width: 'auto', this.cookiesService.setCookie('token', token);
style: { }
'max-width': '90vw',
'max-height': '90vh', getToken(): string | void {
}, return this.cookiesService.getItem('token');
contentStyle: { }
'max-height': '90vh',
height: 'auto', deleteToken(): void {
'max-width': '90vw', this.cookiesService.logout();
overflow: 'auto', // this.router.navigate(['.'], {
'border-radius': '4px', // queryParams: {},
}, // });
baseZIndex: 10000, }
autoZIndex: true,
dismissableMask: true, logout(event?: MouseEvent) {
closeOnEscape: true, if (event) {
showHeader: false, event.preventDefault();
}); }
this.ref.onClose.subscribe((result) => { this.ref = this.dialogService.open(ExitComponent, {
if (result) { width: 'auto',
this.deleteToken(); style: {
this.currentPage = this.pageList[0]; 'max-width': '90vw',
} 'max-height': '90vh',
}); },
} contentStyle: {
} 'max-height': '90vh',
height: 'auto',
'max-width': '90vw',
overflow: 'auto',
'border-radius': '4px',
},
baseZIndex: 10000,
autoZIndex: true,
dismissableMask: true,
closeOnEscape: true,
showHeader: false,
});
this.ref.onClose.subscribe((result) => {
if (result) {
this.deleteToken();
this.currentPage = this.pageList[0];
}
});
}
}

View File

@ -1,99 +1,98 @@
<div class="woocommerce-notices-wrapper"></div> <div class="woocommerce-notices-wrapper"></div>
<ng-container *ngIf="!isCodeConfirm; else confirmPhoneField"> <ng-container *ngIf="!isCodeConfirm; else confirmPhoneField">
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
(submit)="getCode($event)" (submit)="getCode($event)"
> >
<h2>Вход</h2> <h2>Вход</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="phone">Введите Ваш номер телефона</label> <label for="phone">Введите Ваш номер телефона</label>
<p-inputMask <p-inputMask
mask="+7 (999) 999-99-99" mask="+7 (999) 999-99-99"
characterPattern="[0-9]" characterPattern="[0-9]"
styleClass="woocommerce-Input woocommerce-Input--text input-text" styleClass="woocommerce-Input woocommerce-Input--text input-text"
name="phone" name="phone"
type="tel" type="tel"
inputId="phone" inputId="phone"
autocomplete="phone" autocomplete="phone"
placeholder="+7 (000) 000-00-00" placeholder="+7 (000) 000-00-00"
mask="+7 (999) 999-9999" mask="+7 (999) 999-9999"
[unmask]="false" [unmask]="false"
[(ngModel)]="phone" [(ngModel)]="phone"
></p-inputMask> ></p-inputMask>
</p> </p>
<p class="form-row"> <p class="form-row">
<button <button
type="submit" type="submit"
class="woocommerce-button button woocommerce-form-login__submit" class="woocommerce-button button woocommerce-form-login__submit"
name="login" name="login"
value="Получить код" value="Получить код"
[disabled]="loading || phone.length < 10" [disabled]="loading || phone.length < 10"
> >
{{ loading ? "" : "Получить код" }} {{ loading ? "" : "Получить код" }}
<p-progressSpinner <p-progressSpinner
*ngIf="loading" *ngIf="loading"
[style]="{ width: '16px', height: '16px' }" [style]="{ width: '16px', height: '16px' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
</button> </button>
</p> </p>
<div class="decoration-pattern" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></div> <div class="decoration-pattern"></div>
</form> </form>
</ng-container> </ng-container>
<ng-template #confirmPhoneField> <ng-template #confirmPhoneField>
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
action="false" action="false"
(submit)="confirmCode($event)" (submit)="confirmCode($event)"
> >
<h2>Вход</h2> <h2>Вход</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="code"> <label for="code">
Введите 4 последних цифры позвонившего вам номера телефона. На звонок Введите 4 цифры из смс, которое пришло на Ваш номер телефона
отвечать не нужно. </label>
</label> <input
<input pInputText
pInputText type="number"
type="number" pattern="\d*"
pattern="\d*" class="woocommerce-Input woocommerce-Input--text input-text"
class="woocommerce-Input woocommerce-Input--text input-text" name="code"
name="code" id="code"
id="code" autocomplete="code"
autocomplete="code" placeholder="00-00"
placeholder="00-00" [(ngModel)]="code"
[(ngModel)]="code" />
/> </p>
</p> <p class="form-row" style="margin-bottom: 6px;">
<p class="form-row" style="margin-bottom: 6px;"> <button
<button type="submit"
type="submit" class="woocommerce-button button woocommerce-form-login__submit"
class="woocommerce-button button woocommerce-form-login__submit" name="login"
name="login" value="Войти"
value="Войти" [disabled]="loading"
[disabled]="loading" >
> {{ loading ? "" : "Войти" }}
{{ loading ? "" : "Войти" }} <p-progressSpinner
<p-progressSpinner *ngIf="loading"
*ngIf="loading" [style]="{ width: '16px', height: '16px' }"
[style]="{ width: '16px', height: '16px' }" strokeWidth="2"
strokeWidth="2" styleClass="angular-spinner"
styleClass="angular-spinner" ></p-progressSpinner>
></p-progressSpinner> </button>
</button> </p>
</p> <p class="form-row" style="margin-top: 6px">
<p class="form-row" style="margin-top: 6px"> Не пришел код?
Не пришел код? <span class="resend-code" (click)="getCode($event)">Отправить повторно </span>
<span class="resend-code" (click)="getCode($event)">Отправить повторно </span> <span *ngIf="timeLeft">через {{timeLeft}}с</span>
<span *ngIf="timeLeft">через {{timeLeft}}с</span> </p>
</p> <p style="color: red; margin: 0" *ngIf="errorConfirmCode">
<p style="color: red; margin: 0" *ngIf="errorConfirmCode"> Пароль введен неверно
Пароль введен неверно </p>
</p> <div class="decoration-pattern"></div>
<div class="decoration-pattern" style="background: url('./assets/card-decorative-pattern.png') no-repeat;"></div> </form>
</form> </ng-template>
</ng-template>

View File

@ -1,106 +1,109 @@
:host { :host {
display: flex; display: flex;
justify-content: center; justify-content: center;
.woocommerce-form-login__submit { .woocommerce-form-login__submit {
width: 150px; width: 150px;
} }
h2 { h2 {
color: #fff; color: #fff;
width: 100%; width: 100%;
height: 49px; height: 49px;
text-align: left; text-align: left;
padding-top: 13px; padding-top: 13px;
padding-left: 32px; padding-left: 32px;
background: #0d457e; background: #0d457e;
position: relative; position: relative;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
&::before { &::before {
content: ""; content: "";
width: 19px; width: 19px;
height: 4px; height: 4px;
background: #fff; background: #fff;
display: block; display: block;
position: absolute; position: absolute;
left: 0; left: 0;
top: 24px; top: 24px;
} }
} }
.woocommerce-form { .woocommerce-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 10px rgb(0 0 0 / 15%); box-shadow: 0px 0px 10px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
min-height: 262px; min-height: 262px;
max-width: 400px; max-width: 400px;
overflow: auto; overflow: auto;
position: relative; position: relative;
padding-bottom: 10px; padding-bottom: 10px;
} }
.form-row { .form-row {
width: 80%; width: 80%;
margin-bottom: 16px; margin-bottom: 16px;
max-width: 400px; max-width: 400px;
.button { .button {
width: 100%; width: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
height: 45px; height: 45px;
background: #b8deff; background: #b8deff;
border-radius: 15px; border-radius: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
} }
label { label {
display: inline-block; display: inline-block;
margin-bottom: 8px; margin-bottom: 8px;
margin-top: 15px; margin-top: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
} }
.input-text { .input-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
font-size: 1rem; font-size: 1rem;
color: #495057; color: #495057;
background: #ffffff; background: #ffffff;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border: 1px solid #ced4da; border: 1px solid #ced4da;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
} }
.resend-code { .resend-code {
color: #0d457e; color: #0d457e;
font-weight: 600; font-weight: 600;
} }
} }
.decoration-pattern { .decoration-pattern {
width: calc(100% - 24px); width: calc(100% - 24px);
height: 34px; height: 34px;
} background-image: url('../../../../assets/card-decorative-pattern.png');
background-repeat: no-repeat;
background-size: contain;
}
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthComponent } from './auth.component'; import { AuthComponent } from './auth.component';
describe('AuthComponent', () => { describe('AuthComponent', () => {
let component: AuthComponent; let component: AuthComponent;
let fixture: ComponentFixture<AuthComponent>; let fixture: ComponentFixture<AuthComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ AuthComponent ] declarations: [ AuthComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(AuthComponent); fixture = TestBed.createComponent(AuthComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,101 +1,101 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { CookiesService } from 'src/app/services/cookies.service'; import { CookiesService } from 'src/app/services/cookies.service';
import { FormatPhoneService } from 'src/app/services/format-phone.service'; import { FormatPhoneService } from 'src/app/services/format-phone.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({ @Component({
selector: 'app-auth', selector: 'app-auth',
templateUrl: './auth.component.html', templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss'] styleUrls: ['./auth.component.scss']
}) })
export class AuthComponent { export class AuthComponent {
@Input() handleHttpError!: (error: HttpErrorResponse) => void; @Input() handleHttpError!: (error: HttpErrorResponse) => void;
@Output() phoneConfirmed = new EventEmitter<null>(); @Output() phoneConfirmed = new EventEmitter<null>();
public phone: string = ''; public phone: string = '';
public isCodeConfirm = false; public isCodeConfirm = false;
public code: string = ''; public code: string = '';
public phoneToConfirm!: string; public phoneToConfirm!: string;
public loading = false; public loading = false;
public errorConfirmCode: boolean = false; public errorConfirmCode: boolean = false;
timeLeft: number = 0; timeLeft: number = 0;
constructor( constructor(
private phoneFormatter: FormatPhoneService, private phoneFormatter: FormatPhoneService,
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private cookiesService: CookiesService, private cookiesService: CookiesService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private messageService: MessageService, private messageService: MessageService,
) { ) {
} }
getCode(event: any): void { getCode(event: any): void {
event.preventDefault(); event.preventDefault();
this.loading = true; this.loading = true;
this.phoneToConfirm = '+' + this.phone.replace(/\D/g, ''); this.phoneToConfirm = '+' + this.phone.replace(/\D/g, '');
if (this.timeLeft) { if (this.timeLeft) {
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: `Отправить повторно можно через ${this.timeLeft}с`, summary: `Отправить повторно можно через ${this.timeLeft}с`,
}); });
this.loading = false; this.loading = false;
return; return;
} }
this.jsonrpc.rpc({ this.jsonrpc.rpc({
method: 'sendVerifyByPhone', method: 'sendVerifyByPhone',
params: [this.phoneToConfirm] params: [this.phoneToConfirm]
}, RpcService.authService, false).subscribe({ }, RpcService.authService, false).subscribe({
next: (result) => { next: (result) => {
if (result.code === -1) { if (result.code === -1) {
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Произошла ошибка, попробуйте позже!', summary: 'Произошла ошибка, попробуйте позже!',
}); });
} }
if (result.code === 0) { if (result.code === 0) {
this.isCodeConfirm = true; this.isCodeConfirm = true;
this.timeLeft = 60; this.timeLeft = 60;
const interval = setInterval(() => { const interval = setInterval(() => {
if(this.timeLeft > 0) { if(this.timeLeft > 0) {
this.timeLeft--; this.timeLeft--;
} else { } else {
clearInterval(interval); clearInterval(interval);
} }
},1000) },1000)
} }
this.loading = false; this.loading = false;
}, },
error: this.handleHttpError error: this.handleHttpError
} }
); );
} }
confirmCode(event: SubmitEvent): void { confirmCode(event: SubmitEvent): void {
event.preventDefault(); event.preventDefault();
this.jsonrpc.rpc({ this.jsonrpc.rpc({
method: 'getTokenByPhone', method: 'getTokenByPhone',
params: [this.phoneToConfirm, String(this.code)] params: [this.phoneToConfirm, String(this.code)]
}, RpcService.authService, false).subscribe({ }, RpcService.authService, false).subscribe({
next: (result) => { next: (result) => {
if (result.code === 0) { if (result.code === 0) {
this.cookiesService.setCookie('token', result?.data?.token); this.cookiesService.setCookie('token', result?.data?.token);
this.router.navigate(['/auth'], { this.router.navigate(['/auth'], {
queryParams: { queryParams: {
token: result?.data?.token token: result?.data?.token
}, },
}); });
this.phoneConfirmed.emit(null); this.phoneConfirmed.emit(null);
} else { } else {
this.errorConfirmCode = true; this.errorConfirmCode = true;
} }
}, },
error: this.handleHttpError error: this.handleHttpError
} }
); );
} }
} }

View File

@ -1,50 +1,48 @@
<div class="woocommerce-MyAccount-content"> <div class="woocommerce-MyAccount-content">
<h2>{{ currentPage.name }}</h2> <h2>{{ currentPage.name }}</h2>
<!-- <p>{{currentPage.description}}</p> --> <!-- <p>{{currentPage.description}}</p> -->
<div style="display: flex; justify-content: center"> <div style="display: flex; justify-content: center">
<div class="card-container"> <div class="card-container">
<div class="card-container__header"> <div class="card-container__header">
<img src="./assets/logo.svg" alt="Логотип карта" /> <img src="./assets/logo.svg" alt="Логотип карта" />
<span *ngIf="accountData">#{{ accountData.CardNumber }}</span> <span *ngIf="accountData">#{{ accountData.CardNumber }}</span>
</div> </div>
<div class="card-container__decorative-pattern"> <div class="card-container__decorative-pattern">
<!-- <div class="imgs-row"></div> <!-- <div class="imgs-row"></div>
<div class="imgs-row" style="background-position-x: -22px;"></div> <div class="imgs-row" style="background-position-x: -22px;"></div>
<div class="imgs-row"></div> --> <div class="imgs-row"></div> -->
<img src="./assets/card-decorative-pattern.svg" alt="" /> <img src="./assets/card-decorative-pattern.png" alt="" />
</div> </div>
<div class="card-container__content"> <div class="card-container__content">
<div class="info"> <div class="info">
<div *ngIf="accountData" class="row"> <div *ngIf="accountData" class="row">
<span class="key">Имя</span> <span class="key">Имя</span>
<span class="value" *ngIf="userName.length">{{ <span class="value">{{
userName userName && userName.length ? userName : 'Не задано'
}}</span> }}</span>
<span class="value" *ngIf="!userName.length" </div>
>Не задано</span <div *ngIf="accountData" class="row">
> <span class="key">Баланс карты</span>
</div> <span class="value">{{ bonuses }}</span>
<div *ngIf="accountData" class="row"> </div>
<span class="key">Баланс карты</span> </div>
<span class="value">{{ accountData.Bonuses }}</span> <div class="card-container__barcode-container">
</div> <svg
</div> id="barcode"
<div class="card-container__barcode-container"> [ngClass]="{
<svg hidden: loadingBonuses
id="barcode" }"
[ngClass]="{ ></svg>
hidden: loadingBonuses <p-progressSpinner
}" *ngIf="loadingBonuses"
></svg> [style]="{ width: '100%' }"
<p-progressSpinner [ngStyle]="{width: '90px'}"
*ngIf="loadingBonuses" strokeWidth="2"
[style]="{ width: '100%' }" styleClass="angular-spinner"
[ngStyle]="{width: '90px'}" ></p-progressSpinner>
strokeWidth="2" </div>
styleClass="angular-spinner" </div>
></p-progressSpinner> </div>
</div> </div>
</div> <button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button>
</div> </div>
</div>
</div>

View File

@ -1,200 +1,215 @@
:host { :host {
.woocommerce-MyAccount-content { .woocommerce-MyAccount-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
&>h2 { & > h2 {
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
} max-width: 400px;
width: 100%;
&>p { margin: 0 auto;
font-style: normal; text-align: center;
font-weight: 400; }
font-size: 15px;
line-height: 18px; & > p {
margin-top: 8px; font-style: normal;
} font-weight: 400;
font-size: 15px;
.card-container { line-height: 18px;
background: #09467f; margin-top: 8px;
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%); }
border-radius: 15px;
height: 466px; .card-container {
margin-top: 11px; background: var(--main-color);
overflow: hidden; box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
max-width: 400px; border-radius: 15px;
width: 100%; height: 466px;
margin-top: 11px;
&__header { overflow: hidden;
height: 62px; max-width: 400px;
color: #fff; width: 100%;
display: flex;
justify-content: space-between; &__header {
height: 62px;
img { color: #fff;
height: 38px; display: flex;
margin-top: 15px; justify-content: space-between;
}
img {
span { height: 38px;
font-style: normal; margin-top: 15px;
font-weight: 400; }
font-size: 20px;
line-height: 24px; span {
margin-top: 19px; font-style: normal;
margin-right: 18px; font-weight: 400;
} font-size: 20px;
} line-height: 24px;
margin-top: 19px;
&__decorative-pattern { margin-right: 18px;
height: 122px; }
background-color: #fff; }
// padding-top: 9px;
display: flex; &__decorative-pattern {
justify-content: center; height: 122px;
align-items: center; background-color: #fff;
img { // padding-top: 9px;
height: 90px; display: flex;
} justify-content: center;
align-items: center;
// .imgs-row { img {
// background: url("./assets/card-decorative-pattern.png") repeat-x; height: 90px;
// height: 32%; }
// }
} // .imgs-row {
// background: url("./assets/card-decorative-pattern.png") repeat-x;
&__content { // height: 32%;
background-color: #09467f; // }
color: #fff; }
.info { &__content {
height: 79px; background-color: var(--main-color);
border-bottom: solid #fff 1px; color: #fff;
padding: 14px 18px;
.info {
.row { height: 79px;
display: flex; border-bottom: solid #fff 1px;
justify-content: space-between; padding: 14px 18px;
align-items: center;
margin-bottom: 7px; .row {
display: flex;
.key { justify-content: space-between;
font-size: 14px; align-items: center;
line-height: 17px; margin-bottom: 7px;
font-weight: 400;
} .key {
font-size: 14px;
.value { line-height: 17px;
font-size: 18px; font-weight: 400;
font-weight: 600; }
}
} .value {
} font-size: 18px;
} font-weight: 600;
}
&__barcode-container { }
display: flex; }
justify-content: center; }
align-items: center;
padding: 33px 0; &__barcode-container {
display: flex;
#barcode { justify-content: center;
border-radius: 16px; align-items: center;
&.hidden { padding: 33px 0;
display: none;
width: 0; #barcode {
} border-radius: 16px;
} &.hidden {
} display: none;
} width: 0;
}
.card-content { }
position: relative; }
height: 200px; }
width: 100%;
max-width: 400px; .card-content {
-moz-user-select: none; position: relative;
-webkit-user-select: none; height: 200px;
-ms-user-select: none; width: 100%;
-o-user-select: none; max-width: 400px;
user-select: none; -moz-user-select: none;
-webkit-user-select: none;
&__front, -ms-user-select: none;
&__back { -o-user-select: none;
display: block; user-select: none;
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
transition-duration: 0.7s; &__front,
transition-property: transform, opacity; &__back {
color: #fff; display: block;
width: 100%; transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
height: 100%; transition-duration: 0.7s;
background: #fdfdfd; transition-property: transform, opacity;
border-radius: 12px; color: #fff;
border: solid #a3a3a3 1px; width: 100%;
box-shadow: 0 0 3px 1px #fff; height: 100%;
padding: 12px; background: #fdfdfd;
background-size: cover; border-radius: 12px;
} border: solid #a3a3a3 1px;
box-shadow: 0 0 3px 1px #fff;
// &__front { padding: 12px;
// transform: rotateY(0deg); background-size: cover;
// background-image: url(./assets/background.svg); }
// }
// &__front {
&__back { // transform: rotateY(0deg);
position: absolute; // background-image: url(./assets/background.svg);
opacity: 0; // }
top: 0px;
left: 0px; &__back {
transform: rotateY(-180deg); position: absolute;
background: #000; opacity: 0;
} top: 0px;
left: 0px;
&.active_back { transform: rotateY(-180deg);
>.card-content__front { background: #000;
transform: rotateY(180deg); }
opacity: 0;
} &.active_back {
> .card-content__front {
>.card-content__back { transform: rotateY(180deg);
opacity: 1; opacity: 0;
transform: rotateY(0deg); }
}
} > .card-content__back {
opacity: 1;
&__logo { transform: rotateY(0deg);
height: 30px; }
display: flex; }
align-items: center;
justify-content: space-between; &__logo {
height: 30px;
.title { display: flex;
color: #03d1be; align-items: center;
font-weight: 600; justify-content: space-between;
letter-spacing: 2px;
} .title {
} color: #03d1be;
font-weight: 600;
&__footer { letter-spacing: 2px;
display: flex; }
justify-content: space-between; }
align-items: flex-end;
position: absolute; &__footer {
bottom: 12px; display: flex;
width: calc(100% - 24px); justify-content: space-between;
align-items: flex-end;
.count { position: absolute;
font-size: 22px; bottom: 12px;
} width: calc(100% - 24px);
}
} .count {
font-size: 22px;
.explanation { }
font-size: 12px; }
margin-bottom: 8px; }
}
} .explanation {
} font-size: 12px;
margin-bottom: 8px;
}
.add-to-wallet {
height: 36px;
border-radius: 8px;
background: var(--main-color);
border-color: var(--main-color);
color: #fff;
max-width: 400px;
width: 100%;
margin: 12px auto 0;
}
}
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BonusProgramComponent } from './bonus-program.component'; import { BonusProgramComponent } from './bonus-program.component';
describe('BonusProgramComponent', () => { describe('BonusProgramComponent', () => {
let component: BonusProgramComponent; let component: BonusProgramComponent;
let fixture: ComponentFixture<BonusProgramComponent>; let fixture: ComponentFixture<BonusProgramComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ BonusProgramComponent ] declarations: [ BonusProgramComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(BonusProgramComponent); fixture = TestBed.createComponent(BonusProgramComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -0,0 +1,193 @@
import { Component, Inject, OnInit } from '@angular/core';
import { lastValueFrom } from 'rxjs';
import {
orderStatuses,
PageList,
PageListWithBonus,
} from 'src/app/app.constants';
import {
BonusProgramAccount,
Page,
Purchase,
Transaction,
} from 'src/app/interface/data';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import * as moment from 'moment-timezone';
import * as barcode from 'jsbarcode';
import { environment } from 'src/environments/environment';
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
import { CookiesService } from 'src/app/services/cookies.service';
import { DOCUMENT } from '@angular/common';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-bonus-program',
templateUrl: './bonus-program.component.html',
styleUrls: ['./bonus-program.component.scss'],
})
export class BonusProgramComponent implements OnInit {
public accountData!: BonusProgramAccount;
public purchases: Purchase[] = [];
public loadingBonuses: boolean = false;
readonly orderStatuses = orderStatuses;
readonly moment = moment;
readonly pageList = environment.hasBonusProgram
? PageListWithBonus
: PageList;
public currentPage: Page = this.pageList[1];
public userName: string = '';
public deviceType: 'ios' | 'android' | null = null;
public bonuses: number = 0
constructor(
private jsonrpc: JsonrpcService,
private appleWallet: AppleWalletService,
private cookiesService: CookiesService,
@Inject(DOCUMENT) private document: Document,
private http: HttpClient,
private messageService: MessageService,
) {}
ngOnInit(): void {
this.getAccountData();
this.getTypeDevice();
}
getTypeDevice() {
const userAgent = window.navigator.userAgent.toLowerCase();
const ios = /iphone|ipod|ipad/.test(userAgent);
this.deviceType = ios ? 'ios' : 'android';
}
async getAccountData(): Promise<void> {
this.loadingBonuses = true;
this.jsonrpc
.rpc(
{
method: 'getAdditionalInfo',
params: [],
},
RpcService.authService,
true
)
.subscribe({
next: (res) => {
this.userName = res?.data?.first_name;
},
error: (err) => {
console.error('Error: ', err);
},
});
const getAccount = await lastValueFrom(
this.jsonrpc.rpc(
{
method: 'GetAccounts',
params: [],
},
RpcService.bonusService
)
);
this.accountData = getAccount && getAccount['Cards'][0];
this.bonuses = this.accountData.BonusProgramAccounts?.reduce((previous, {Bonuses}) => {
return previous + Bonuses
}, 0) || this.accountData.Bonuses
this.loadingBonuses = false;
if (this.accountData) {
barcode('#barcode')
.options({ font: 'OCR-B' }) // Will affect all barcodes
.EAN13(`${this.accountData.CardNumber}`.padStart(12, '0'), {
fontSize: 18,
textMargin: 0,
})
.render();
}
const getTransactions = (
await lastValueFrom(
this.jsonrpc.rpc(
{
method: 'GetAccountTransactions',
params: [],
},
RpcService.bonusService
)
)
)
if (!getTransactions) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return
}
const transactions: Transaction[] = getTransactions && getTransactions['Transactions'];
const getPurchases = (
await lastValueFrom(
this.jsonrpc.rpc(
{
method: 'GetAccountPurchase',
params: [],
},
RpcService.bonusService
)
)
)
if (!getPurchases) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return
}
const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
this.purchases = purchases && purchases.map<Purchase>((purchase) => {
const id = purchase.ID.slice(0, 36).toLowerCase();
purchase.Transactions = transactions.filter((transaction) => {
const same = transaction.Purchase === id;
transaction.HasPurchase = same;
return same;
});
return purchase;
});
transactions && transactions.forEach((transaction) => {
if (!transaction.HasPurchase) {
this.purchases.push({
ID: transaction.ID,
PurchaseDate: transaction.Date,
Transactions: [transaction],
IsSingleTransaction: true,
transactionSum: 0
});
}
});
this.purchases = this.purchases && this.purchases.sort((a, b) => {
return moment(a.PurchaseDate).date() - moment(b.PurchaseDate).date();
});
}
async addCardToWallet(e: any) {
e.preventDefault();
const token = this.cookiesService.getItem('token');
const accountData = (
await lastValueFrom(
this.jsonrpc.rpc(
{
method: 'getTokenData',
params: [],
},
RpcService.authService,
true
)
)
).data;
if (token && accountData.user_id) {
this.appleWallet.generateCard(token, accountData.user_id).subscribe({
next: (res: any) => {
this.document.location.href = res.url;
},
error: (err) => {
console.log('Error: ', err);
},
});
}
}
}

View File

@ -1,88 +1,89 @@
<div class="orders"> <div class="orders">
<table <table
class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table" class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table"
> >
<thead> <thead>
<tr> <tr>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date"
> >
<span class="nobr">Дата</span> <span class="nobr">Дата</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status"
> >
<span class="nobr">Место покупки</span> <span class="nobr">Место покупки</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-total" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-total"
> >
<span class="nobr">Сумма чека</span> <span class="nobr">Сумма чека</span>
</th> </th>
<th <th
class="woocommerce-orders-table__header woocommerce-orders-table__header-order-actions" class="woocommerce-orders-table__header woocommerce-orders-table__header-order-actions"
> >
<span class="nobr">Бонусы</span> <span class="nobr">Бонусы</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
*ngFor="let purchase of purchasesShortArray" *ngFor="let purchase of purchasesShortArray"
class="woocommerce-orders-table__row woocommerce-orders-table__row--status-processing order" class="woocommerce-orders-table__row woocommerce-orders-table__row--status-processing order"
> >
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date"
data-title="Дата" data-title="Дата"
> >
<time [dateTime]="purchase.PurchaseDate">{{ <time [dateTime]="purchase.PurchaseDate">{{
moment(purchase.PurchaseDate).format("DD.MM.YYYY") moment(purchase.PurchaseDate).format("DD.MM.YYYY")
}}</time> }}</time>
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status"
data-title="Место покупки" data-title="Место покупки"
> >
{{ purchase.Address }} {{ purchase.Address }}
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-total" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-total"
data-title="Сумма чека" data-title="Сумма чека"
> >
<span class="woocommerce-Price-amount amount"> <span class="woocommerce-Price-amount amount">
{{ purchase.CheckSummary }} {{ purchase.CheckSummary }}
</span> </span>
</td> </td>
<td <td
class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-actions" class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-actions"
data-title="Бонусы" data-title="Бонусы"
[ngClass]="{ [ngClass]="{
'red-color': purchase.BonusSummary ? purchase.BonusSummary < 0 : false, 'red-color': purchase.BonusSummary ? purchase.BonusSummary < 0 : false,
'green-color': purchase.BonusSummary ? purchase.BonusSummary > 0 : false 'green-color': purchase.BonusSummary ? purchase.BonusSummary > 0 : false
}" }"
> >
{{ purchase.BonusSummary }} {{ purchase.BonusSummary }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p <p
*ngIf="purchases.length !== purchasesShortArray.length" *ngIf="purchases?.length !== purchasesShortArray?.length"
class="show-more-orders" class="show-more-orders"
(click)="getMoreOrders()" (click)="getMoreOrders()"
> >
Показать больше Показать больше
</p> </p>
<p-progressSpinner <p-progressSpinner
*ngIf="ordersLoadingStatus" *ngIf="ordersLoadingStatus"
[style]="{ width: '100%' }" [style]="{ display: 'block' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
<p <p
*ngIf="purchases.length === 0 && !ordersLoadingStatus" *ngIf="purchases?.length === 0 && !ordersLoadingStatus"
style="width: 100%; text-align: center" class="no-orders"
> style="width: 100%; text-align: center"
Нет заказов >
</p> Нет заказов
</div> </p>
</div>

View File

@ -0,0 +1,76 @@
.show-more-orders {
text-align: center;
cursor: pointer;
color: var(--main-color);
margin-top: 16px;
}
.woocommerce-MyAccount-content {
max-width: 400px;
margin-left: calc(50vw - 200px);
}
.woocommerce-orders-table {
border: 2px solid #dee2e6;
border-bottom: 0;
border-radius: 0.25rem;
border-collapse: separate;
text-align: left;
margin-top: 8px;
width: 100%;
max-width: 400px;
margin-left: auto;
margin-right: auto;
thead {
display: none;
}
.woocommerce-orders-table {
&__cell {
padding: 0.7rem 1.5rem;
border-bottom: 2px solid #dee2e6;
text-align: right !important;
display: block;
&::before {
content: attr(data-title) ": ";
font-weight: 700;
float: left;
}
}
&__row {
display: block;
&:nth-child(even) {
background: #ebebeb;
}
}
// &__cell-order-actions::before {
// display: none;
// }
&__cell-order-actions {
&.red-color {
color: #ed0000;
}
&.green-color {
color: #00a700;
}
}
&__cell-order-number a {
text-decoration: none;
color: #009688;
}
}
.woocommerce-button {
display: inline-block;
color: #ffffff;
background-color: #009688;
padding: 13px;
border-radius: 4px;
text-decoration: none;
width: 100%;
text-align: center;
}
}
.no-orders {
width: 100%;
text-align: center;
margin-top: 16px;
}

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrdersComponent } from './orders.component'; import { OrdersComponent } from './orders.component';
describe('OrdersComponent', () => { describe('OrdersComponent', () => {
let component: OrdersComponent; let component: OrdersComponent;
let fixture: ComponentFixture<OrdersComponent>; let fixture: ComponentFixture<OrdersComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ OrdersComponent ] declarations: [ OrdersComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(OrdersComponent); fixture = TestBed.createComponent(OrdersComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,59 +1,67 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Purchase } from 'src/app/interface/data'; import { Purchase } from 'src/app/interface/data';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { MessageService } from 'primeng/api';
@Component({
selector: 'app-orders', @Component({
templateUrl: './orders.component.html', selector: 'app-orders',
styleUrls: ['./orders.component.scss'] templateUrl: './orders.component.html',
}) styleUrls: ['./orders.component.scss'],
export class OrdersComponent implements OnInit { })
@Input() handleHttpError!: (error: HttpErrorResponse) => void; export class OrdersComponent implements OnInit {
public lastViewOrder: number = 3; @Input() handleHttpError!: (error: HttpErrorResponse) => void;
public ordersLoadingStatus: boolean = true; public lastViewOrder: number = 3;
readonly moment = moment; public ordersLoadingStatus: boolean = true;
public purchases: Purchase[] = []; readonly moment = moment;
public purchasesShortArray: Purchase[] = []; public purchases: Purchase[] = [];
public purchasesShortArray: Purchase[] = [];
constructor(
private route: ActivatedRoute, constructor(
private router: Router, private route: ActivatedRoute,
private jsonrpc: JsonrpcService, private router: Router,
) { } private jsonrpc: JsonrpcService,
private messageService: MessageService,
ngOnInit(): void { ) {}
this.getOrders()
} ngOnInit(): void {
this.getOrders();
async getOrders(){ }
const purchases: Purchase[] = (await lastValueFrom(
this.jsonrpc.rpc( async getOrders() {
{ const getPurchases = await lastValueFrom(
method: 'GetAccountPurchase', this.jsonrpc.rpc(
params: [] {
}, method: 'GetAccountPurchase',
RpcService.bonusService params: [],
)))['Purchases']; },
RpcService.bonusService
this.purchases = purchases.map<Purchase>((purchase) => { )
const id = purchase.ID.slice(0,36).toLowerCase(); );
// purchase.Transactions = transactions.filter((transaction) => { if (!getPurchases) {
// const same = transaction.Purchase === id; this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
// transaction.HasPurchase = same; return
// return same; }
// }); const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
return purchase;
}); this.purchases = purchases && purchases.map<Purchase>((purchase) => {
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder) const id = purchase.ID.slice(0, 36).toLowerCase();
this.ordersLoadingStatus = false; // purchase.Transactions = transactions.filter((transaction) => {
} // const same = transaction.Purchase === id;
// transaction.HasPurchase = same;
getMoreOrders() { // return same;
this.lastViewOrder += 4; // });
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder); return purchase;
} });
} this.purchasesShortArray = this.purchases && this.purchases.slice(0, this.lastViewOrder);
this.ordersLoadingStatus = false;
}
getMoreOrders() {
this.lastViewOrder += 4;
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder);
}
}

View File

@ -1,25 +1,25 @@
<div class="ref-system"> <div class="ref-system">
<qrcode <!-- <qrcode
*ngIf="!loading" *ngIf="!loading"
[qrdata]="refUrl" [qrdata]="refUrl"
[width]="256" [width]="256"
[errorCorrectionLevel]="'M'" [errorCorrectionLevel]="'M'"
></qrcode> ></qrcode> -->
<p-progressSpinner <p-progressSpinner
*ngIf="loading" *ngIf="loading"
[style]="{ width: '100%' }" [style]="{ width: '100%' }"
strokeWidth="2" strokeWidth="2"
styleClass="angular-spinner" styleClass="angular-spinner"
></p-progressSpinner> ></p-progressSpinner>
<!-- <share-buttons <!-- <share-buttons
[theme]="'modern-dark'" [theme]="'modern-dark'"
[include]="['telegram', 'whatsapp', 'vk', 'copy', 'facebook', 'twitter']" [include]="['telegram', 'whatsapp', 'vk', 'copy', 'facebook', 'twitter']"
[show]="4" [show]="4"
[url]="refUrl" [url]="refUrl"
style="display: flex; justify-content: center;" style="display: flex; justify-content: center;"
></share-buttons> --> ></share-buttons> -->
<div class="share-container" *ngIf="!loading"> <div class="share-container" *ngIf="!loading">
<div class="copy" type="button" (click)="copyUrl()">Скопировать ссылку</div> <div class="copy" type="button" (click)="copyUrl()">Скопировать ссылку</div>
<div class="share" type="button" (click)="share()"><img src="./assets/share.svg" alt="share"></div> <div class="share" type="button" (click)="share()"><img src="./assets/share.svg" alt="share"></div>
</div> </div>
</div> </div>

View File

@ -1,31 +1,33 @@
:host { :host {
.ref-system { .ref-system {
.share-container { .share-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
height: 50px; height: 50px;
max-width: 400px; max-width: 400px;
margin: 0 auto; margin: 0 auto;
.share { .share {
width: 50px; width: 50px;
background-color: #09467f; background-color: var(--main-color);
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
img { cursor: pointer;
height: 26px; img {
} height: 26px;
} }
.copy { }
background-color: #09467f; .copy {
color: #fff; background-color: var(--main-color);
border-radius: 8px; color: #fff;
width: calc(100% - 66px); border-radius: 8px;
display: flex; width: calc(100% - 66px);
justify-content: center; display: flex;
align-items: center; justify-content: center;
} align-items: center;
} cursor: pointer;
} }
}
}
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RefSystemComponent } from './ref-system.component'; import { RefSystemComponent } from './ref-system.component';
describe('RefSystemComponent', () => { describe('RefSystemComponent', () => {
let component: RefSystemComponent; let component: RefSystemComponent;
let fixture: ComponentFixture<RefSystemComponent>; let fixture: ComponentFixture<RefSystemComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ RefSystemComponent ] declarations: [ RefSystemComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(RefSystemComponent); fixture = TestBed.createComponent(RefSystemComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,64 +1,71 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import * as barcode from 'jsbarcode'; import * as barcode from 'jsbarcode';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
@Component({ @Component({
selector: 'app-ref-system', selector: 'app-ref-system',
templateUrl: './ref-system.component.html', templateUrl: './ref-system.component.html',
styleUrls: ['./ref-system.component.scss'] styleUrls: ['./ref-system.component.scss']
}) })
export class RefSystemComponent implements OnInit { export class RefSystemComponent implements OnInit {
public refUrl: string = `${environment.defaultUrl}/?refCardNumber=` public refUrl: string = `${environment.defaultUrl}/?refUserId=`
public loading: boolean = true; public loading: boolean = true;
constructor( constructor(
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
private messageService: MessageService private messageService: MessageService
) { } ) { }
async ngOnInit() { async ngOnInit() {
const accountData = (await lastValueFrom( const accountData = (await lastValueFrom(
this.jsonrpc.rpc({ this.jsonrpc
method: 'GetAccounts', .rpc(
params: [] {
}, method: 'getTokenData',
RpcService.bonusService params: [],
)))['Cards'][0] },
this.refUrl += accountData.CardNumber RpcService.authService,
this.loading = false true
} )
)).data
share() {
if (navigator.share) { this.refUrl += accountData.user_id
navigator.share({ this.loading = false
title: document.title, }
text: "Fashion Logica",
url: this.refUrl share() {
}) if (navigator.share) {
.then(() => console.log('Successful share')) navigator.share({
.catch((error) => { title: document.title,
console.log('Error sharing:', error) text: "Tasty Coffee",
}); url: this.refUrl
} })
} .then(() => console.log('Successful share'))
.catch((error) => {
copyUrl() { console.log('Error sharing:', error)
navigator.clipboard.writeText(this.refUrl) });
.then(() => { }
this.messageService.add({ }
severity: 'custom',
summary: 'Ссылка скопирована!', copyUrl() {
}); navigator.clipboard.writeText(this.refUrl)
}) .then(() => {
.catch(err => { this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'custom',
summary: 'Произошла ошибка!', summary: 'Ссылка скопирована!',
}); });
}); })
} .catch(err => {
this.messageService.clear();
} this.messageService.add({
severity: 'error',
summary: 'Произошла ошибка!',
});
});
}
}

View File

@ -1,87 +1,87 @@
<form <form
class="woocommerce-form woocommerce-form-login login" class="woocommerce-form woocommerce-form-login login"
action="false" action="false"
(ngSubmit)="saveData()" (ngSubmit)="saveData()"
[formGroup]="userDataFormGroup" [formGroup]="userDataFormGroup"
> >
<h2>Личные данные</h2> <h2>Личные данные</h2>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<label for="phone">Заполните поля анкеты</label> <label for="phone">Заполните поля анкеты</label>
<input <input
formControlName="first_name" formControlName="first_name"
pInputText pInputText
type="text" type="text"
class="woocommerce-Input woocommerce-Input--text input-text" class="woocommerce-Input woocommerce-Input--text input-text"
name="first_name" name="first_name"
id="first_name" id="first_name"
placeholder="Имя" placeholder="Имя"
/> />
</p> </p>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<!-- <p-inputMask <!-- <p-inputMask
type="text" type="text"
class="woocommerce-Input woocommerce-Input--text" class="woocommerce-Input woocommerce-Input--text"
name="birthdate" name="birthdate"
inputId="birthdate" inputId="birthdate"
placeholder="Дата рождения (00.00.00)" placeholder="Дата рождения (00.00.00)"
mask="99.99.99" mask="99.99.99"
[styleClass]="datevalid ? '' : 'invalid'" [styleClass]="datevalid ? '' : 'invalid'"
[(ngModel)]="birthdate" [(ngModel)]="birthdate"
[ngModelOptions]="{standalone: true}" [ngModelOptions]="{standalone: true}"
></p-inputMask> --> ></p-inputMask> -->
<input <input
type="date" type="date"
formControlName="birthdate" formControlName="birthdate"
class="woocommerce-Input woocommerce-Input--text input-text" class="woocommerce-Input woocommerce-Input--text input-text"
pInputText pInputText
name="birthdate" name="birthdate"
id="birthdate" id="birthdate"
placeholder="Дата рождения" placeholder="Дата рождения"
/> />
</p> </p>
<p <p
class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide" class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide"
> >
<span class="select-wrapper" [ngClass]="{ <span class="select-wrapper" [ngClass]="{
'select-wrapper': true, 'select-wrapper': true,
'hidden': userDataFormGroup.value.gender.length 'hidden': userDataFormGroup.value.gender.length
}"> }">
<select formControlName="gender" name="gender" id="gender" required> <select formControlName="gender" name="gender" id="gender" required>
<!-- <option hidden value="" disabled selected>Выберите пол</option> --> <!-- <option hidden value="" disabled selected>Выберите пол</option> -->
<option value="Мужской">Мужской</option> <option value="Мужской">Мужской</option>
<option value="Женский">Женский</option> <option value="Женский">Женский</option>
</select> </select>
</span> </span>
</p> </p>
<p class="form-row" style="margin-bottom: 6px"> <p class="form-row" style="margin-bottom: 6px">
<button <button
type="submit" type="submit"
class="woocommerce-button button woocommerce-form-login__submit" class="woocommerce-button button woocommerce-form-login__submit"
name="save" name="save"
value="Сохранить" value="Сохранить"
[disabled]="false" [disabled]="false"
> >
Сохранить Сохранить
</button> </button>
</p> </p>
<span class="policy" <span class="policy"
>Нажимая кнопку, Вы соглашаетесь с Политикой обработки персональных >Нажимая кнопку, Вы соглашаетесь с Политикой обработки персональных
данных.</span данных.</span
> >
<div class="decoration-pattern"> <div class="decoration-pattern">
<img <img
style="height: 34px" style="height: 34px"
src="./assets/card-decorative-pattern.svg" src="./assets/card-decorative-pattern.png"
alt="" alt=""
/> />
<img <img
style="height: 16px; margin-left: 2px" style="height: 16px; margin-left: 2px"
src="./assets/card-decorative-pattern.svg" src="./assets/card-decorative-pattern.png"
alt="" alt=""
/> />
</div> </div>
</form> </form>

View File

@ -1,189 +1,189 @@
:host { :host {
display: flex; display: flex;
justify-content: center; justify-content: center;
.woocommerce-form-login__submit { .woocommerce-form-login__submit {
width: 150px; width: 150px;
} }
h2 { h2 {
color: #fff; color: #fff;
width: 100%; width: 100%;
height: 49px; height: 49px;
text-align: left; text-align: left;
padding-top: 13px; padding-top: 13px;
padding-left: 32px; padding-left: 32px;
background: #0d457e; background: #0d457e;
position: relative; position: relative;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 20px; font-size: 20px;
line-height: 24px; line-height: 24px;
&::before { &::before {
content: ""; content: "";
width: 19px; width: 19px;
height: 4px; height: 4px;
background: #fff; background: #fff;
display: block; display: block;
position: absolute; position: absolute;
left: 0; left: 0;
top: 24px; top: 24px;
} }
} }
.woocommerce-form { .woocommerce-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 10px rgb(0 0 0 / 15%); box-shadow: 0px 0px 10px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
min-height: 262px; min-height: 262px;
max-width: 400px; max-width: 400px;
overflow: auto; overflow: auto;
position: relative; position: relative;
padding-bottom: 10px; padding-bottom: 10px;
} }
.form-row { .form-row {
width: 80%; width: 80%;
margin-bottom: 16px; margin-bottom: 16px;
max-width: 400px; max-width: 400px;
.button { .button {
width: 100%; width: 100%;
font-weight: 600; font-weight: 600;
letter-spacing: 2px; letter-spacing: 2px;
border: none; border: none;
height: 45px; height: 45px;
background: #b8deff; background: #b8deff;
border-radius: 15px; border-radius: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
} }
label { label {
display: inline-block; display: inline-block;
margin-bottom: 8px; margin-bottom: 8px;
margin-top: 15px; margin-top: 15px;
color: #000; color: #000;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 18px; font-size: 18px;
line-height: 22px; line-height: 22px;
} }
.input-text { .input-text {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
color: #495057; color: #495057;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
padding-left: 13px; padding-left: 13px;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 4px; border-radius: 4px;
width: 100%; width: 100%;
height: 45px; height: 45px;
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #B8DEFF; border: 1px solid #B8DEFF;
border-radius: 15px; border-radius: 15px;
font-size: 14px; font-size: 14px;
text-align: left; text-align: left;
&.ng-dirty.ng-invalid { &.ng-dirty.ng-invalid {
border-color: red; border-color: red;
} }
&.ng-invalid.ng-touched { &.ng-invalid.ng-touched {
border-color: red; border-color: red;
} }
} }
select { select {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol"; "Segoe UI Symbol";
background: #FFFFFF; background: #FFFFFF;
border: 1px solid #B8DEFF; border: 1px solid #B8DEFF;
border-radius: 15px; border-radius: 15px;
width: 100%; width: 100%;
height: 45px; height: 45px;
padding: 0 10px; padding: 0 10px;
font-size: 14px; font-size: 14px;
text-align: left; text-align: left;
color: #495057; color: #495057;
// option[value=""][disabled] { // option[value=""][disabled] {
// display: none; // display: none;
// } // }
&.ng-dirty.ng-invalid { &.ng-dirty.ng-invalid {
border-color: red; border-color: red;
} }
&.ng-invalid.ng-touched { &.ng-invalid.ng-touched {
border-color: red; border-color: red;
} }
} }
select:required:invalid { select:required:invalid {
color: #6c757d; color: #6c757d;
} }
} }
.policy { .policy {
width: 80%; width: 80%;
text-align: center; text-align: center;
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 12px; font-size: 12px;
line-height: 15px; line-height: 15px;
color: #BDBDBD; color: #BDBDBD;
} }
.decoration-pattern { .decoration-pattern {
margin-top: 12px; margin-top: 12px;
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
margin-left: 0; margin-left: 0;
width: 92%; width: 92%;
} }
input[type="date"]::before { input[type="date"]::before {
content: attr(placeholder); content: attr(placeholder);
width: 100%; width: 100%;
color: #6c757d; color: #6c757d;
} }
input[type="date"]:focus::before, input[type="date"]:focus::before,
input[type="date"].ng-valid::before { input[type="date"].ng-valid::before {
display: none; display: none;
} }
.select-wrapper { .select-wrapper {
position: relative; position: relative;
&::before { &::before {
content: 'Выберите пол'; content: 'Выберите пол';
position: absolute; position: absolute;
top: 3px; top: 3px;
left: 13px; left: 13px;
height: 100%; height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol" !important; "Segoe UI Symbol" !important;
font-family: inherit; font-family: inherit;
color: #6c757d; color: #6c757d;
font-size: 14px; font-size: 14px;
} }
&.hidden::before { &.hidden::before {
display: none; display: none;
} }
} }
/* hide our custom/fake placeholder text when in focus to show the default /* hide our custom/fake placeholder text when in focus to show the default
* 'mm/dd/yyyy' value and when valid to show the users' date of birth value. * 'mm/dd/yyyy' value and when valid to show the users' date of birth value.
*/ */
// input[type="date"]:focus::before, // input[type="date"]:focus::before,
// input[type="date"]:valid::before { display: none } // input[type="date"]:valid::before { display: none }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserDataComponent } from './user-data.component'; import { UserDataComponent } from './user-data.component';
describe('UserDataComponent', () => { describe('UserDataComponent', () => {
let component: UserDataComponent; let component: UserDataComponent;
let fixture: ComponentFixture<UserDataComponent>; let fixture: ComponentFixture<UserDataComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ UserDataComponent ] declarations: [ UserDataComponent ]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(UserDataComponent); fixture = TestBed.createComponent(UserDataComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,77 +1,110 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { UserDataForm } from 'src/app/interface/data'; import { lastValueFrom } from 'rxjs';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { UserDataForm } from 'src/app/interface/data';
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
@Component({ import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
selector: 'app-user-data',
templateUrl: './user-data.component.html', @Component({
styleUrls: ['./user-data.component.scss'] selector: 'app-user-data',
}) templateUrl: './user-data.component.html',
export class UserDataComponent implements OnInit { styleUrls: ['./user-data.component.scss']
public userDataFormGroup!: FormGroup; })
public userData: UserDataForm = { export class UserDataComponent implements OnInit {
first_name: '', public userDataFormGroup!: FormGroup;
birthdate: '', public userData: UserDataForm = {
gender: '', first_name: '',
} birthdate: '',
gender: '',
}
constructor(
private jsonRpcService: JsonrpcService,
private messageService: MessageService, constructor(
) { } private jsonRpcService: JsonrpcService,
private messageService: MessageService,
ngOnInit(): void { private appleWallet: AppleWalletService,
this.createUserDataForm({...this.userData}) private jsonrpc: JsonrpcService,
this.jsonRpcService.rpc({ ) { }
method: 'getAdditionalInfo',
params: [] ngOnInit(): void {
}, RpcService.authService, true).subscribe({ this.createUserDataForm({...this.userData})
next: (res) => { this.jsonRpcService.rpc({
const { first_name, birthdate, gender } = res.data method: 'getAdditionalInfo',
this.userData = { first_name, birthdate, gender } params: []
this.createUserDataForm({...this.userData}) }, RpcService.authService, true).subscribe({
}, next: (res) => {
error: (err) => { if (res.code === 45) {
console.error('Error: ', err) this.messageService.add({severity:'warn', summary:'Данные не найдены, заполните их на этой странице'});
} return
}); }
if (!res.data) {
} this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return
createUserDataForm(val: UserDataForm) { }
this.userDataFormGroup = new FormGroup({ const { first_name, birthdate, gender } = res.data
first_name: new FormControl(val.first_name, [Validators.required, Validators.minLength(2)]), this.userData = { first_name, birthdate, gender }
birthdate: new FormControl(val.birthdate, [Validators.required]), this.createUserDataForm({...this.userData})
gender: new FormControl(val.gender ? val.gender : '', [Validators.required]), },
}) error: (err) => {
} console.error('Error: ', err)
}
saveData() { });
if (this.userDataFormGroup.invalid) {
this.markFormGroupTouched(this.userDataFormGroup) }
return
} createUserDataForm(val: UserDataForm) {
this.userData = this.userDataFormGroup.value as UserDataForm this.userDataFormGroup = new FormGroup({
this.jsonRpcService.rpc({ first_name: new FormControl(val.first_name, [Validators.required, Validators.minLength(2)]),
method: 'updateAdditionalInfo', birthdate: new FormControl(val.birthdate, [Validators.required]),
params: [this.userData] gender: new FormControl(val.gender ? val.gender : '', [Validators.required]),
}, RpcService.authService, true).subscribe({ })
next: () => { }
this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'});
}, saveData() {
error: (err) => { if (this.userDataFormGroup.invalid) {
console.error('Error: ', err) this.markFormGroupTouched(this.userDataFormGroup)
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'}); return
} }
}) this.userData = this.userDataFormGroup.value as UserDataForm
} this.jsonRpcService.rpc({
method: 'updateAdditionalInfo',
markFormGroupTouched(formGroup: FormGroup) { params: [this.userData]
(<any>Object).values(formGroup.controls).forEach((control: FormControl) => { }, RpcService.authService, true).subscribe({
control.markAsDirty(); next: async () => {
}); this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'});
} const accountData = (await lastValueFrom(
} this.jsonrpc
.rpc(
{
method: 'getTokenData',
params: [],
},
RpcService.authService,
true
)
)).data
if (accountData.user_id) {
this.appleWallet.reloadCard(accountData.user_id).subscribe({
next: (value) => {
console.log(value);
},
error: (err) => {
console.error(err);
}
})
}
},
error: (err) => {
console.error('Error: ', err)
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
}
})
}
markFormGroupTouched(formGroup: FormGroup) {
(<any>Object).values(formGroup.controls).forEach((control: FormControl) => {
control.markAsDirty();
});
}
}

View File

@ -0,0 +1,82 @@
<app-navbar title="Карта гостя" (backEvent)="logout()"></app-navbar>
<div class="guest-card">
<div class="top-info">
<div class="top-info__level">
<p id="level">Уровень {{ currentLvl }}</p>
<p id="level-percent">Кэшбек {{ currentLvlPeriod.percent }}%</p>
</div>
<p class="top-info__bonus">{{ getBalanceAmount(customerInfo?.walletBalances) }} бонусов</p>
</div>
<div class="guest-card__qr" (click)="qrCodeClick()">
<ng-container *ngIf="customerInfo">
<qr-code
[value]="customerInfo?.phone.substr(2) || 'Данные не найдены'"
[margin]="0"
[size]="qrCodeSize"
errorCorrectionLevel="M"
></qr-code>
</ng-container>
<ng-container *ngIf="!customerInfo">
<ng-container
*ngTemplateOutlet="spinner; context: { $implicit: 85 }"
></ng-container>
</ng-container>
</div>
<div class="guest-card__user-description">
<p>
<ng-container *ngIf="currentLvlPeriod.end">
Осталось купить на сумму <span class="price">{{ currentLvlPeriod.end - (purchaseData.currentAmount || 0) + 1 }}</span>
рублей, тогда кэшбек будет <span class="percent">{{ getNextLevel().percent }}%</span> с
{{ purchaseData.currentPeriod[1].locale('ru').format('D MMMM')}}
</ng-container>
<ng-container *ngIf="!currentLvlPeriod.end">
У Вас последний уровень бонусной программы. Процент начисляемых бонусов:
{{currentLvlPeriod.percent}}%
</ng-container>
</p>
</div>
<span id="bonuses-condition"></span>
<!-- <div class="guest-card__level-info"> -->
<!-- <ng-container *ngIf="!purchaseData.$loading"> -->
<!-- <ng-container *ngIf="currentLvlPeriod.end"> -->
<!-- <\!-- <input -\-> -->
<!-- <\!-- type="range" -\-> -->
<!-- <\!-- [value]="purchaseData.currentAmount" -\-> -->
<!-- <\!-- [min]="currentLvlPeriod.start" -\-> -->
<!-- <\!-- [max]="currentLvlPeriod.end" -\-> -->
<!-- <\!-- [step]="0.01" -\-> -->
<!-- <\!-- disabled="true" -\-> -->
<!-- <\!-- [ngStyle]="{ -\-> -->
<!-- <\!-- 'background-size': ((purchaseData.currentAmount! - currentLvlPeriod.start) / (currentLvlPeriod.end - currentLvlPeriod.start + 1)) * 100 + '% 100%' -\-> -->
<!-- <\!-- }" -\-> -->
<!-- <\!-- /> -\-> -->
<!-- </ng-container> -->
<!-- <ng-container *ngIf="!currentLvlPeriod.end"> -->
<!-- <h2> -->
<!-- У Вас последний уровень бонусной программы. Процент начисляемых бонусов: {{currentLvlPeriod.percent}}% -->
<!-- </h2> -->
<!-- </ng-container> -->
<!-- </ng-container> -->
<!-- <ng-container *ngIf="purchaseData.$loading"> -->
<!-- <ng-container -->
<!-- *ngTemplateOutlet="spinner; context: { $implicit: 48 }" -->
<!-- ></ng-container> -->
<!-- </ng-container> -->
<!-- </div> -->
<app-last-order
[lastOrder]="lastPurchase"
[loading]="purchaseData.$loading"
></app-last-order>
<a class="guest-card__loyalty-program" routerLink="loyality-program"
>Подробнее о правилах <br />
Программы лояльности</a
>
</div>
<ng-template #spinner let-diameter>
<mat-spinner [strokeWidth]="3" [diameter]="diameter"></mat-spinner>
</ng-template>

View File

@ -0,0 +1,222 @@
:host {
.guest-card {
display: flex;
flex-direction: column;
align-items: center;
max-width: 600px;
margin: 0 auto;
.top-info {
padding: 8px 0 0;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #292929;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border: 1px solid #000000;
border-top: 1px solid #888888;
&__level {
width: 100%;
display: flex;
align-items: center;
justify-content: space-around;
font-size: 16px;
}
& p {
color: var(--button-color);
}
p.top-info__bonus{
font-size: 20px;
}
}
&__qr {
margin: 10px;
padding: 10px;
width: fit-content;
background-image: linear-gradient(
#fff 33%,
transparent 0px,
transparent 67%,
#fff 0px
),
linear-gradient(
90deg,
#ffe 33%,
transparent 0px,
transparent 66%,
#fff 0px
),
linear-gradient(#fff 33%, transparent 0px, transparent 67%, #fff 0),
linear-gradient(90deg, #fff 33%, transparent 0, transparent 66%, #fff 0);
background-size: 1px 100%, 100% 1px, 1px 100%, 100% 1px;
background-position: 0 0, 0 0, 100% 100%, 100% 100%;
background-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
cursor: pointer;
}
&__user-description {
background-color: #292929;
padding: 14px 24px;
p {
width: 60%;
text-align: center;
margin: 0 auto;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 19px;
letter-spacing: -0.5px;
color: var(--text-color);
}
.price,
.percent {
font-weight: bold;
}
}
&__purchases-description {
margin: 0;
padding: 14px 24px;
width: 100%;
text-align: center;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 19px;
letter-spacing: -0.5px;
grid-template-columns: calc(100% - 24px) 24px;
span {
color: #219653;
}
}
&__level-info {
padding-top: 36px;
padding-left: 36px;
padding-right: 36px;
display: flex;
flex-direction: column;
align-items: center;
h2 {
font-style: normal;
font-family: Montserrat;
font-weight: 700;
font-size: 17px;
line-height: 22px;
text-align: center;
letter-spacing: -0.408px;
}
input[type="range"] {
-webkit-appearance: none;
width: 100%;
height: 6px;
border-radius: 5px;
display: block;
position: relative;
background: #231f20;
box-shadow: 0px 0px 3px #f2c94c59;
background-image: linear-gradient(#f2c94c, #f2c94c);
background-size: 70% 100%;
background-repeat: no-repeat;
&::before,
&::after {
content: " ";
display: block;
width: 16px;
height: 16px;
border-radius: 100%;
position: absolute;
top: -5px;
}
&::before {
background-color: #f2c94c;
left: 0px;
}
&::after {
background-color: #f2c94c;
right: 0px;
}
&::-webkit-slider-thumb {
-webkit-appearance: none;
background: #f2c94c;
width: 16px;
height: 16px;
border-radius: 100%;
display: none;
}
&::-ms-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #f2c94c;
cursor: ew-resize;
box-shadow: 0 0 2px 0 #555;
transition: background 0.3s ease-in-out;
}
&::-moz-range-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #f2c94c;
cursor: ew-resize;
box-shadow: 0 0 2px 0 #555;
transition: background 0.3s ease-in-out;
}
}
}
&__download-app {
width: 100%;
position: relative;
margin-top: 32px;
display: flex;
justify-content: flex-end;
img {
width: 100%;
max-width: calc(100% - 16px);
}
}
&__loyalty-program {
text-align: center;
color: #7F2061;
font-style: normal;
font-weight: bold;
font-size: 12px;
line-height: 16px;
text-decoration: none;
margin: 17px 0 22px;
}
}
}
app-accordion {
ul {
li {
list-style-type: disc;
margin-left: 16px;
}
}
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GuestCardComponent } from './guest-card.component';
describe('GuestCardComponent', () => {
let component: GuestCardComponent;
let fixture: ComponentFixture<GuestCardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GuestCardComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(GuestCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,193 @@
import { Component, OnInit } from '@angular/core';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { Router } from '@angular/router';
import { Observable } from 'rxjs';
import { ExitComponent } from 'src/app/components/exit/exit.component';
import { CookiesService } from 'src/app/services/cookies.service';
import { WpJsonService } from 'src/app/services/wp-json.service';
import { environment } from 'src/environments/environment';
import moment from 'moment';
import { Purchase, lvlPeriod } from 'src/app/interface/data';
import { lvlPeriods } from 'src/app/app.constants';
interface Moment extends moment.Moment {}
export interface IPurchaseData {
currentPeriod: Moment[];
lastPeriod: Moment[];
lastPurchases: Purchase[];
currentPurchases: Purchase[];
lastAmount?: number;
currentAmount?: number;
$loading: boolean;
}
@Component({
selector: 'app-guest-card',
templateUrl: './guest-card.component.html',
styleUrls: ['./guest-card.component.scss'],
})
export class GuestCardComponent implements OnInit {
public qrCodeSize: number = 85;
private isQrCodeClicked: boolean = false;
public customerInfo!: any;
public purchases!: Purchase[];
public lastPurchase!: Purchase;
public purchaseData: IPurchaseData = {
currentPeriod: [],
lastPeriod: [],
lastPurchases: [],
currentPurchases: [],
$loading: true,
get currentAmount():number {
const amount = this.currentPurchases.reduce((accumulator, currentValue) => {
if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
return accumulator + currentValue.orderSum!;
}, 0);
return amount * 1
},
get lastAmount():number {
const amount = this.lastPurchases.reduce((accumulator, currentValue) => {
if (currentValue.transactionType !== 'PayFromWallet') return accumulator;
return accumulator + currentValue.orderSum!;
}, 0);
return amount * 1
}
};
public discountLevel: number = 4.2;
public lvlPeriods: lvlPeriod[] = lvlPeriods;
public currentLvlPeriod!: lvlPeriod;
public currentLvl: number = 1;
constructor(
private _bottomSheet: MatBottomSheet,
private cookiesService: CookiesService,
private router: Router,
private wpJsonService: WpJsonService
) {}
ngOnInit(): void {
const token = this.cookiesService.getItem('token');
this.getCurrentQuarterOfYear();
this.wpJsonService
.getCustomerInfo(
environment.systemId,
token || '',
environment.icardProxy
)
.subscribe({
next: (value) => {
this.customerInfo = value.customer_info;
this.getPurchases().subscribe((value) => {
this.purchases = value[this.customerInfo?.id].filter(
(purchase: Purchase) =>
[
'PayFromWallet',
'CancelPayFromWallet',
'RefillWallet',
'RefillWalletFromOrder'
].includes(purchase.transactionType || '')
);
this.lastPurchase = this.purchases.filter(
(purchase: Purchase) =>
[
'PayFromWallet',
'RefillWalletFromOrder'
].includes(purchase.transactionType || '')
)[0]
this.purchaseData.lastPurchases = this.purchases.filter((value: Purchase) => {
return moment(value.transactionCreateDate).isBetween(this.purchaseData.lastPeriod[0], this.purchaseData.lastPeriod[1])
})
this.purchaseData.currentPurchases = this.purchases.filter((value: Purchase) => {
return moment(value.transactionCreateDate).isBetween(this.purchaseData.currentPeriod[0], this.purchaseData.currentPeriod[1])
})
const currentAmount = this.purchaseData.currentAmount || 0
const index = this.lvlPeriods.findIndex((item) => item.start <= currentAmount && currentAmount <= (item.end || Infinity))!
if(index != -1) {
this.currentLvlPeriod = this.lvlPeriods[index];
this.currentLvl = index + 1;
this.purchaseData.$loading = false;
}
});
},
});
}
getNextLevel(): lvlPeriod {
if(this.currentLvl == this.lvlPeriods.length) {
return lvlPeriods[this.currentLvl - 1];
}
return lvlPeriods[this.currentLvl];
}
qrCodeClick() {
this.isQrCodeClicked = !this.isQrCodeClicked;
this.qrCodeSize = this.isQrCodeClicked ? 180 : 85;
}
deleteToken(): void {
this.cookiesService.logout();
}
logout() {
const bottomSheet = this._bottomSheet.open(ExitComponent);
bottomSheet.afterDismissed().subscribe({
next: (val) => {
if (val) {
this.deleteToken();
this.router.navigate(['/login']);
}
},
});
}
getPurchases(
start: Date | Moment = moment().subtract(1, 'months').startOf('month'),
end: Date | Moment = moment()
): Observable<any> {
const token = this.cookiesService.getItem('token');
const delta = moment(end).diff(moment(start), 'days');
return this.wpJsonService.getTransactions(
environment.systemId,
token ?? '',
environment.icardProxy
);
}
getCurrentQuarterOfYear() {
const quarters = [
[
moment().subtract(1, 'years').endOf('year').subtract(3, 'months'),
moment().subtract(1, 'years').endOf('year'),
],
[moment().startOf('year'), moment().startOf('year').add(3, 'months')],
[
moment().startOf('year').add(3, 'months'),
moment().startOf('year').add(6, 'months'),
],
[
moment().startOf('year').add(6, 'months'),
moment().startOf('year').add(9, 'months'),
],
[
moment().startOf('year').add(9, 'months'),
moment().startOf('year').add(12, 'months'),
],
];
for (let i = 0; i < 4; i++) {
if (moment().isBetween(quarters[i][0], quarters[i][1])) {
this.purchaseData.lastPeriod = quarters[i - 1];
this.purchaseData.currentPeriod = quarters[i];
}
}
}
getBalanceAmount(loyaltyPrograms: any[]) {
return loyaltyPrograms.reduce((accumulator, currentValue) => {
return accumulator + currentValue.balance;
}, 0);
}
}

View File

@ -0,0 +1,107 @@
<app-navbar
*ngIf="phoneForm.value.phone && !isShowNumber"
[title]="phoneForm.value.phone"
(backEvent)="backToPhoneForm()"
[ngStyle]="{
position: 'absolute',
top: 0
}"
></app-navbar>
<h1>Участвуй в программе лояльности COFFEE LIKE</h1>
<p class="description">Начни получать бонусы прямо сейчас</p>
<form
*ngIf="isShowNumber; else smsCode"
(ngSubmit)="submitNumber()"
[formGroup]="phoneForm"
>
<!-- <div class="input-container"> -->
<mat-form-field appearance="outline">
<mat-label>Ваше имя</mat-label>
<input formControlName="name" matInput type="text" />
</mat-form-field>
<!-- </div> -->
<div class="input-container">
<!-- <label for="number">Номер телефона</label>
<input id="number" type="text" placeholder="Введите номер" /> -->
<mat-form-field appearance="outline">
<mat-label>Номер телефона</mat-label>
<ngx-mat-intl-tel-input
formControlName="phone"
[enablePlaceholder]="true"
[enableSearch]="false"
[onlyCountries]="['ru', 'kg', 'by', 'kz', 'fi', 'de']"
[preferredCountries]="['ru']"
name="phone"
#phone
>
</ngx-mat-intl-tel-input>
</mat-form-field>
</div>
<p class="offer">
Используя приложение, вы принимаете условия в <span>соглашениях</span> и
соглашаетесь на получение рекламно-информационных сообщений
</p>
<button [disabled]="phoneForm.invalid">Принять участие</button>
</form>
<ng-template #smsCode>
<h2>Введите код из SMS</h2>
<form class="code-form" [formGroup]="codeForm" (ngSubmit)="submitCode()">
<div class="inputs-container">
<label class="box"
><input
class="field"
id="field"
type="tel"
placeholder="•"
#field
[appFocusNextInput]="inputFocusEmitter"
formControlName="code"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field1"
type="tel"
placeholder="•"
#field1
[appFocusNextInput]="inputFocusEmitter"
formControlName="code1"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field2"
type="tel"
placeholder="•"
#field2
[appFocusNextInput]="inputFocusEmitter"
formControlName="code2"
maxlength="1"
/></label>
<label class="box"
><input
class="field"
id="field3"
type="tel"
placeholder="•"
#field3
[appFocusNextInput]="inputFocusEmitter"
formControlName="code3"
maxlength="1"
/></label>
</div>
<button>Войти</button>
</form>
<p class="resend-code">
Не пришло SMS?<br />
<ng-container *ngIf="timeLeft">
Отправим повторно через {{timeLeft}}с
</ng-container>
<ng-container *ngIf="!timeLeft">
<span class="resend" (click)="submitNumber()">Отправить повторно</span>
</ng-container>
</p>
</ng-template>

View File

@ -0,0 +1,214 @@
:host {
padding-top: 48px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 600px;
margin: 0 auto 52px;
h1 {
margin-top: 20px;
width: 302px;
font-style: normal;
font-weight: 700;
font-size: 17px;
line-height: 22px;
text-align: center;
letter-spacing: -0.408px;
}
h2 {
margin-top: 44px;
font-style: normal;
font-weight: 700;
font-size: 20px;
line-height: 24px;
text-align: center;
letter-spacing: 0.38px;
}
.description {
width: 180px;
font-style: normal;
font-weight: 400;
font-size: 15px;
line-height: 20px;
text-align: center;
letter-spacing: -0.24px;
margin-top: 16px;
}
form {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 35px;
.offer {
margin-top: 10px;
padding: 0 16px;
font-family: "Gowun Dodum";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 17px;
text-align: center;
span {
color: #13a538;
}
}
.input-container {
position: relative;
width: 100%;
label {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
color: #6a737c;
text-align: left;
position: absolute;
top: 10px;
left: 16px;
}
input {
width: 100%;
padding: 24px 16px 8px;
background-color: #252323;
margin-bottom: 16px;
border: none;
border-top: solid #6a737c 1px;
border-bottom: solid #6a737c 1px;
color: #6a737c;
font-style: normal;
font-weight: 400;
font-size: 22px;
line-height: 28px;
}
}
button {
height: 46px;
width: calc(100% - 32px);
padding: 10px 24px;
background: #28af49;
border-radius: 6px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
border: none;
letter-spacing: -0.408px;
color: #ffffff;
font-style: normal;
font-weight: 700;
font-size: 17px;
line-height: 22px;
&:disabled {
background-color: #344a3a;
}
}
}
.code-form {
width: 100%;
// input {
// width: 100%;
// padding: 24px 16px 8px;
// background-color: #252323;
// margin-bottom: 16px;
// border: none;
// border-top: solid #6a737c 1px;
// border-bottom: solid #6a737c 1px;
// color: #6a737c;
// font-style: normal;
// font-weight: 400;
// font-size: 22px;
// line-height: 28px;
// }
.inputs-container {
width: 102px;
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 40px;
// code
.box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 42px;
width: 42px;
border-radius: 6px;
// box-shadow: 0 0 6px 1px hsla(240, 54%, 61%, 0.2);
overflow: hidden;
will-change: transform;
}
.box:focus-within {
box-shadow: 0 0 6px 1px rgba($color: #28af49, $alpha: 0.2),
0 0 0 2px rgba($color: #28af49, $alpha: 0.6);
}
.box::before,
.box::after {
content: "";
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
border-radius: 6px;
overflow: hidden;
}
.box::before {
// background: hsl(240, 54%, 97%);
z-index: 1;
transition: background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
}
.box::after {
transform: translateY(100%);
background-color: hsl(145, 0%, 42%);
opacity: 0;
z-index: 10;
transition: transform 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
opacity 450ms cubic-bezier(0.25, 0.01, 0.25, 1),
background-color 450ms cubic-bezier(0.25, 0.01, 0.25, 1);
}
.field {
position: relative;
border: 0;
outline: 0;
font-size: 25.21px;
line-height: 42px;
color: #fff;
background-color: transparent;
text-align: center;
z-index: 100;
}
.field::placeholder {
color: #fff;
}
}
}
.resend-code {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 16px;
text-align: center;
margin-top: 23px;
.resend {
cursor: pointer;
}
}
}
mat-form-field {
width: 100%;
}

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

Some files were not shown because too many files have changed in this diff Show More