Compare commits

...

193 Commits

Author SHA1 Message Date
f4612ad996 Merge remote-tracking branch 'remotes/origin/coffee-like-test' into coffee-like 2023-09-05 16:31:48 +04:00
756a033504 исправил кр код при авторизации 2023-09-05 15:04:50 +04:00
nikolay
92e72b2d70 Merge branch 'coffee-like-test' into coffee-like 2023-08-21 09:46:18 +04:00
nikolay
29f8ca5230 dev #14384 Правка ошибок отображения WPA КофеЛайк: remove asterisk from name input 2023-08-21 09:46:05 +04:00
nikolay
9134acb910 Merge branch 'coffee-like-test' into coffee-like 2023-08-18 09:06:35 +04:00
nikolay
a62f60df40 dev #14384 Правка ошибок отображения WPA КофеЛайк: remove banner 2023-08-18 09:06:12 +04:00
nikolay
2d2a613e80 Merge branch 'coffee-like-test' into coffee-like 2023-07-26 14:00:28 +04:00
nikolay
172a21b7f7 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix description 2023-07-26 14:00:13 +04:00
nikolay
85b260b4cd dev #14384 Правка ошибок отображения WPA КофеЛайк: add second build 2023-07-26 11:44:25 +04:00
nikolay
64585fd4ef dev #14384 Правка ошибок отображения WPA КофеЛайк: revert url 2023-07-26 10:32:22 +04:00
nikolay
e02a1aa916 Merge branch 'coffee-like-test' into coffee-like 2023-07-26 10:30:24 +04:00
nikolay
a1668de544 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix error handling 2023-07-26 10:30:07 +04:00
nikolay
8c9a7ed1c2 Merge branch 'coffee-like-test' into coffee-like 2023-07-25 16:52:13 +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
nikolay
377f72ebe7 Merge branch 'coffee-like-test' into coffee-like 2023-07-14 13:18:09 +04:00
nikolay
9d00a32d4f dev #14717 Добавить меню в андрод WPAL: remove back arrow from guest card 2023-07-11 15:48:02 +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
b14995786d return ruble sign in last order price 2023-07-06 13:21:00 +04:00
tosandriy
6168cd574f 10910 2023-07-06 12:40:42 +04:00
nikolay
492a87d310 remove "git pull" from Jenkinsfile 2023-07-06 12:23:56 +04:00
nikolay
028c28b385 git pull --rebase 2023-07-06 12:12:39 +04:00
nikolay
ee0792b954 dev #dev #14696 Перенос WPA приложения кофелайка тест на прод: add git pull to Jenkinsfile 2023-07-06 12:11:17 +04:00
nikolay
97ce3c9e55 Merge branch 'coffee-like-test' into coffee-like 2023-07-06 10:58:20 +04:00
nikolay
50333514af dev #14384 Правка ошибок отображения WPA КофеЛайк: revert banner 2023-07-06 10:40:28 +04:00
nikolay
5f522bad47 dev #14384 Правка ошибок отображения WPA КофеЛайк: some fixes 2023-07-04 15:53:49 +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
656e41b8cf dev #14384 Правка ошибок отображения WPA КофеЛайк: change user info data 2023-07-03 11:52:56 +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
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
cawa
26ca44d4b7 Jenkinsfile 2023-06-26 15:20:23 +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
0db84a982e fix https 2023-06-09 11:17:05 +04:00
nikolay
55b2c0c2ff fix jenkinsfile 2023-06-09 11:14:19 +04:00
nikolay
4ca921b3f7 qwe 2023-06-09 11:12:15 +04:00
nikolay
52bb80f081 dev #14384dev #14384 Правка ошибок отображения WPA КофеЛайк: fix endpoint 2023-06-09 11:10:44 +04:00
nikolay
fb7602a110 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix build command 2023-06-02 10:44:52 +04:00
nikolay
1811ed08c3 dev #14384 Правка ошибок отображения WPA КофеЛайк: remove test word from webmanifest 2023-06-02 10:36:23 +04:00
nikolay
0c05afbc49 dev #14384 Правка ошибок отображения WPA КофеЛайк: fix title 2023-06-01 13:15:35 +04:00
436cddbc67 сделал сборку в правильную папку 2023-06-01 11:31:07 +04:00
695b43d087 test 2023-06-01 11:28:40 +04:00
nikolay
a9b7694a35 dev #14444 Добавить скрипт яндекс метрики и вебмастер к WPA приложению Кофелайк: add script 2023-06-01 10:32:26 +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
290 changed files with 15324 additions and 6155 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

58
.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'
sh label: '', script: 'npm run build2'
}
}
}
private boolean lastCommitIsBumpCommit() {
lastCommit = sh([script: 'git log -1', returnStdout: true])
if (lastCommit.contains("Author: jenkins")) {
return true
} else {
return false
}
}

View File

@ -1,4 +1,4 @@
# FashionLogica # CoffeeLike
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.

View File

@ -1,15 +1,17 @@
{ {
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
"fashion-logica": { "coffee-like": {
"projectType": "application", "projectType": "application",
"schematics": { "schematics": {
"@schematics/angular:component": { "@schematics/angular:component": {
"style": "scss" "style": "scss"
} }
}, },
"root": "", "root": "",
"sourceRoot": "src", "sourceRoot": "src",
"prefix": "app", "prefix": "app",
@ -17,8 +19,8 @@
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "/var/www/lk/fashion-logica", "outputPath": "/var/www/html/lk.crm4retail.ru/coffee-like",
"baseHref": "/fashion-logica/", "baseHref": "/",
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "main": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
@ -28,9 +30,12 @@
"src/favicon.ico", "src/favicon.ico",
"src/assets", "src/assets",
"src/manifest.webmanifest", "src/manifest.webmanifest",
"src/firebase-messaging-sw.js" "src/firebase-messaging-sw.js",
"src/sw-master.js",
"src/sw-custom.js"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css", "node_modules/primeng/resources/themes/bootstrap4-light-blue/theme.css",
"node_modules/primeicons/primeicons.css", "node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/primeng.min.css", "node_modules/primeng/resources/primeng.min.css",
@ -64,6 +69,28 @@
], ],
"outputHashing": "all" "outputHashing": "all"
}, },
"production2": {
"outputPath": "/var/www/html/lk.crm4retail.ru/coffee-like2",
"budgets": [
{
"type": "initial",
"maximumWarning": "4mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment2.prod.ts"
}
],
"outputHashing": "all"
},
"development": { "development": {
"buildOptimizer": false, "buildOptimizer": false,
"optimization": false, "optimization": false,
@ -79,10 +106,11 @@
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "fashion-logica:build:production" "browserTarget": "coffee-like:build:production"
}, },
"development": { "development": {
"browserTarget": "fashion-logica:build:development" "browserTarget": "coffee-like:build:development",
"proxyConfig": "proxy.confi.json"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
@ -90,7 +118,7 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "fashion-logica:build" "browserTarget": "coffee-like:build"
} }
}, },
"test": { "test": {
@ -108,6 +136,7 @@
"src/manifest.webmanifest" "src/manifest.webmanifest"
], ],
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/purple-green.css",
"src/styles.scss" "src/styles.scss"
], ],
"scripts": [] "scripts": []

File diff suppressed because it is too large Load Diff

62
angular/package.json Normal file
View File

@ -0,0 +1,62 @@
{
"name": "coffee-like",
"version": "0.0.2",
"scripts": {
"ng": "ng",
"start": "ng serve --host 192.168.0.14",
"build": "ng build --aot --configuration production --output-hashing none",
"watch": "ng build --watch --configuration development",
"build2": "ng build --aot --configuration production2 --output-hashing none",
"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/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",
"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

@ -20,16 +20,16 @@ describe('AppComponent', () => {
expect(app).toBeTruthy(); expect(app).toBeTruthy();
}); });
it(`should have as title 'fashion-logica'`, () => { it(`should have as title 'coffee-like-test'`, () => {
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('coffee-like-test');
}); });
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('coffee-like-test app is running!');
}); });
}); });

View File

@ -0,0 +1,91 @@
import { Component, HostListener, 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';
import { pwaInstalled } from './utils';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
title = 'Coffee Like';
public static pwaPrompt: any;
public static pwaInstalled: boolean = true;
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);
}
};
}
});
}
@HostListener('window:beforeinstallprompt', ['$event'])
onBeforeInstallPrompt(e: any) {
e.preventDefault();
AppComponent.pwaPrompt = e;
if (AppComponent.pwaPrompt) {
AppComponent.pwaInstalled = false;
}
}
@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.
AppComponent.pwaPrompt = e;
AppComponent.pwaInstalled = true;
}
getAdditionalInfo() {
return this.jsonRpcService.rpc(
{
method: 'getAdditionalInfo',
params: [],
},
RpcService.authService,
true
);
}
ngOnInit(): void {
pwaInstalled().then((res) => {
AppComponent.pwaInstalled = res;
});
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

@ -1,15 +1,14 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router'; import { LoyalityProgramComponent } from './pages/loyality-program/loyality-program.component';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { MainComponent } from './pages/main/main.component'; import { MainComponent } from './pages/main/main.component';
import { NavbarComponent } from './components/navbar/navbar.component'; import { NavbarComponent } from './components/navbar/navbar.component';
import { CardComponent } from './components/card/card.component'; import { CardComponent } from './components/card/card.component';
import {InputMaskModule} from "primeng/inputmask"; import { InputMaskModule } from 'primeng/inputmask';
import { AuthComponent } from './pages/account/auth/auth.component'; import { AuthComponent } from './pages/account/auth/auth.component';
import {ProgressSpinnerModule} from "primeng/progressspinner"; import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AccountComponent } from './pages/account/account.component'; import { AccountComponent } from './pages/account/account.component';
import { ExitComponent } from './components/exit/exit.component'; import { ExitComponent } from './components/exit/exit.component';
@ -17,21 +16,40 @@ import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { DialogService } from 'primeng/dynamicdialog'; import { DialogService } from 'primeng/dynamicdialog';
import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component'; import { BonusProgramComponent } from './pages/account/bonus-program/bonus-program.component';
import { OrdersComponent } from './pages/account/orders/orders.component';
import { OrderInfoComponent } from './components/order-info/order-info.component'; import { OrderInfoComponent } from './components/order-info/order-info.component';
import { ServiceWorkerModule } from '@angular/service-worker'; import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
import { AngularFireModule } from '@angular/fire/compat'; import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireMessagingModule } from '@angular/fire/compat/messaging'; import { AngularFireMessagingModule } from '@angular/fire/compat/messaging';
import {ToastModule} from 'primeng/toast'; import { ToastModule } from 'primeng/toast';
import { MessageService } from 'primeng/api'; import { MessageService } from 'primeng/api';
import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component'; import { FooterButtonsComponent } from './components/footer-buttons/footer-buttons.component';
import { UserDataComponent } from './pages/account/user-data/user-data.component'; import { UserDataComponent } from './pages/account/user-data/user-data.component';
import { RefSystemComponent } from './pages/account/ref-system/ref-system.component'; import { RefSystemComponent } from './pages/account/ref-system/ref-system.component';
import { QRCodeModule } from 'angularx-qrcode';
import { ShareButtonsModule } from 'ngx-sharebuttons/buttons'; import { ShareButtonsModule } from 'ngx-sharebuttons/buttons';
import { ShareIconsModule } from 'ngx-sharebuttons/icons'; import { ShareIconsModule } from 'ngx-sharebuttons/icons';
import { MessagingService } from './services/messaging.service'; 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({ @NgModule({
declarations: [ declarations: [
@ -43,44 +61,61 @@ import { MessagingService } from './services/messaging.service';
AccountComponent, AccountComponent,
ExitComponent, ExitComponent,
BonusProgramComponent, BonusProgramComponent,
OrdersComponent,
OrderInfoComponent, OrderInfoComponent,
FooterButtonsComponent, FooterButtonsComponent,
UserDataComponent, UserDataComponent,
RefSystemComponent RefSystemComponent,
NotFoundComponent,
GuestCardComponent,
AccordionComponent,
LastOrderComponent,
InviteFriendsComponent,
FooterComponent,
SocialMediaButtonsComponent,
LoginComponent,
// FocusNextInputDirective,
LoyalityProgramComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
RouterModule.forRoot([
{
path: '**',
component: MainComponent
}
]),
InputMaskModule, InputMaskModule,
ProgressSpinnerModule, ProgressSpinnerModule,
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
BrowserModule, BrowserModule,
ServiceWorkerModule.register('ngsw-worker.js', { ServiceWorkerModule.register('/sw-master.js', {
enabled: environment.production, enabled: environment.production,
// Register the ServiceWorker as soon as the application is stable // Register the ServiceWorker as soon as the application is stable
// or after 30 seconds (whichever comes first). // or after 30 seconds (whichever comes first).
registrationStrategy: 'registerWhenStable:30000' registrationStrategy: 'registerWhenStable:30000',
}), }),
AngularFireModule.initializeApp(environment.firebase), AngularFireModule.initializeApp(environment.firebase),
AngularFireMessagingModule, AngularFireMessagingModule,
ToastModule, ToastModule,
ReactiveFormsModule, ReactiveFormsModule,
QRCodeModule,
ShareButtonsModule.withConfig({ ShareButtonsModule.withConfig({
debug: true debug: true,
}), }),
ShareIconsModule ShareIconsModule,
MatIconModule,
QrCodeModule,
NgxMatIntlTelInputComponent,
MatFormFieldModule,
MatInputModule,
MatSnackBarModule,
MatBottomSheetModule,
MatProgressSpinnerModule,
DirectivesModule,
], ],
providers: [DialogService, MessageService, MessagingService ], providers: [
bootstrap: [AppComponent] DialogService,
MessageService,
MessagingService,
{ provide: MatBottomSheetRef, useValue: {} },
{ provide: MAT_BOTTOM_SHEET_DATA, useValue: {} },
],
bootstrap: [AppComponent],
}) })
export class AppModule { } 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

@ -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

@ -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,12 +1,11 @@
<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"

View File

@ -15,9 +15,9 @@
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;

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: "Coffee Like Test",
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

@ -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

@ -16,7 +16,6 @@
.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;

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,50 @@
import {
Directive,
ElementRef,
HostListener,
OnInit,
Renderer2,
} from '@angular/core';
import { MessageService } from 'primeng/api';
import { AppComponent } from '../app.component';
@Directive({
selector: '[appDownloadApp]',
})
export class DownloadAppDirective implements OnInit {
constructor(
private messageService: MessageService,
public renderer: Renderer2,
private el: ElementRef
) {}
ngOnInit(): void {
if (AppComponent.pwaInstalled) {
this.el.nativeElement.style.display = 'none';
}
}
@HostListener('click', ['$event'])
async downloadApp(event: MouseEvent) {
if (!AppComponent.pwaPrompt) {
this.messageService.clear();
this.messageService.add({
severity: 'error',
summary: 'Не поддерживается в Вашем браузере!',
});
return;
}
AppComponent.pwaPrompt.prompt();
AppComponent.pwaPrompt.userChoice.then((res: any) => {
if (res.outcome === 'accepted') {
this.messageService.clear();
this.messageService.add({
severity: 'success',
summary: 'Спасибо за установку!',
});
}
AppComponent.pwaPrompt = 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,230 @@
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 CurrentInfo {
current_cashback: number;
current_level: number;
}
export interface LastPurchase {
last_purchase_date: string;
last_purchase_sum: number;
}
export interface NextLevel {
cashback: number;
next_level: number;
sum_for_next_level: number;
}
export interface UserInfo {
current_level_and_cashback: CurrentInfo;
last_purchase?: LastPurchase;
id: string;
phone: string;
walletBalances: number;
next_level: NextLevel;
}
export interface ResponseError {
code: number;
msg: string;
}

View File

@ -1,5 +1,10 @@
<div class="woocommerce"> <div
<div [ngClass]="{
woocommerce: true,
'auth-page': currentPage.code === PageCode.Auth
}"
>
<!-- <div
*ngIf="currentPage.code !== PageCode.Auth" *ngIf="currentPage.code !== PageCode.Auth"
class="top-left-attribute" class="top-left-attribute"
></div> ></div>
@ -40,6 +45,7 @@
> >
<div class="container"> <div class="container">
<img <img
style="width: 36px"
src="{{ './assets/menu-icons/' + page.resName + '.png' }}" src="{{ './assets/menu-icons/' + page.resName + '.png' }}"
alt="Иконка меню" alt="Иконка меню"
/> />
@ -50,6 +56,22 @@
</div> </div>
</li> </li>
</ng-container> </ng-container>
<li
class="woocommerce-MyAccount-navigation-link download-app"
appDownloadApp
>
<div class="container">
<img
style="width: 36px"
src="./assets/menu-icons/download-app.png"
alt="Иконка меню"
/>
<div class="menu-item-info">
<a href="#">Установить приложение</a>
<p>{{ page.description }}</p>
</div>
</div>
</li>
<li <li
class="woocommerce-MyAccount-navigation-link" class="woocommerce-MyAccount-navigation-link"
(click)="logout($event)" (click)="logout($event)"
@ -62,12 +84,11 @@
</div> </div>
</li> </li>
</ul> </ul>
</nav> </nav> -->
<span <span
class="version" class="version"
[ngClass]="{ [ngClass]="{
version: true, version: true
bottom: currentPage.code === PageCode.Auth
}" }"
> >
v{{ version }} v{{ version }}

View File

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

View File

@ -9,6 +9,7 @@ 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({ @Component({
selector: 'app-account', selector: 'app-account',
@ -70,10 +71,24 @@ export class AccountComponent implements OnInit {
this.currentPage = this.pageList[1]; this.currentPage = this.pageList[1];
} }
refSystem() { async refSystem() {
const additionalInfo = (await lastValueFrom(
this.jsonRpcService.rpc({
method: 'getAdditionalInfo',
params: []
}, RpcService.authService, true)
)).data
this.route.queryParams.subscribe((params) => { this.route.queryParams.subscribe((params) => {
if (params['refCardNumber']) { if (params['refUserId']) {
this.refSystemId = params['refCardNumber']; if (additionalInfo.refSystem?.code.length) {
this.messageService.add({
severity: 'custom',
summary:
'Вы уже зарегестрированы в реферальной программе!',
});
return;
}
this.refSystemId = params['refUserId'];
this.jsonRpcService this.jsonRpcService
.rpc( .rpc(
{ {
@ -93,7 +108,7 @@ export class AccountComponent implements OnInit {
next: () => { next: () => {
this.router.navigate([], { this.router.navigate([], {
queryParams: { queryParams: {
refCardNumber: null, refUserId: null,
}, },
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
}); });
@ -117,6 +132,10 @@ export class AccountComponent implements OnInit {
changePage(event: MouseEvent, page: Page): void { changePage(event: MouseEvent, page: Page): void {
event.preventDefault(); event.preventDefault();
if (page.resName === 'download-app') {
return
}
this.currentPage = page; this.currentPage = page;
// let params = new HttpParams(); // let params = new HttpParams();
// params = params.append('activePage', this.currentPage.code); // params = params.append('activePage', this.currentPage.code);
@ -147,7 +166,7 @@ export class AccountComponent implements OnInit {
} }
deleteToken(): void { deleteToken(): void {
this.cookiesService.deleteCookie('token'); this.cookiesService.logout();
// this.router.navigate(['.'], { // this.router.navigate(['.'], {
// queryParams: {}, // queryParams: {},
// }); // });

View File

@ -40,7 +40,7 @@
></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>
@ -54,8 +54,7 @@
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
@ -94,6 +93,6 @@
<p style="color: red; margin: 0" *ngIf="errorConfirmCode"> <p style="color: red; margin: 0" *ngIf="errorConfirmCode">
Пароль введен неверно Пароль введен неверно
</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-template> </ng-template>

View File

@ -102,5 +102,8 @@
.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

@ -11,22 +11,19 @@
<!-- <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"
>Не задано</span
>
</div> </div>
<div *ngIf="accountData" class="row"> <div *ngIf="accountData" class="row">
<span class="key">Баланс карты</span> <span class="key">Баланс карты</span>
<span class="value">{{ accountData.Bonuses }}</span> <span class="value">{{ bonuses }}</span>
</div> </div>
</div> </div>
<div class="card-container__barcode-container"> <div class="card-container__barcode-container">
@ -47,4 +44,5 @@
</div> </div>
</div> </div>
</div> </div>
<button *ngIf="deviceType == 'ios'" class="add-to-wallet" (click)="addCardToWallet($event)">Добавить в Apple Wallet</button>
</div> </div>

View File

@ -3,14 +3,18 @@
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%;
margin: 0 auto;
text-align: center;
} }
&>p { & > p {
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-size: 15px; font-size: 15px;
@ -19,7 +23,7 @@
} }
.card-container { .card-container {
background: #09467f; background: var(--main-color);
box-shadow: 0px 0px 5px rgb(0 0 0 / 15%); box-shadow: 0px 0px 5px rgb(0 0 0 / 15%);
border-radius: 15px; border-radius: 15px;
height: 466px; height: 466px;
@ -67,7 +71,7 @@
} }
&__content { &__content {
background-color: #09467f; background-color: var(--main-color);
color: #fff; color: #fff;
.info { .info {
@ -154,12 +158,12 @@
} }
&.active_back { &.active_back {
>.card-content__front { > .card-content__front {
transform: rotateY(180deg); transform: rotateY(180deg);
opacity: 0; opacity: 0;
} }
>.card-content__back { > .card-content__back {
opacity: 1; opacity: 1;
transform: rotateY(0deg); transform: rotateY(0deg);
} }
@ -196,5 +200,16 @@
font-size: 12px; font-size: 12px;
margin-bottom: 8px; 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

@ -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

@ -67,7 +67,7 @@
</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()"
> >
@ -75,12 +75,13 @@
</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"
class="no-orders"
style="width: 100%; text-align: center" style="width: 100%; text-align: center"
> >
Нет заказов Нет заказов

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

@ -5,11 +5,12 @@ 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({ @Component({
selector: 'app-orders', selector: 'app-orders',
templateUrl: './orders.component.html', templateUrl: './orders.component.html',
styleUrls: ['./orders.component.scss'] styleUrls: ['./orders.component.scss'],
}) })
export class OrdersComponent implements OnInit { export class OrdersComponent implements OnInit {
@Input() handleHttpError!: (error: HttpErrorResponse) => void; @Input() handleHttpError!: (error: HttpErrorResponse) => void;
@ -23,24 +24,31 @@ export class OrdersComponent implements OnInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private jsonrpc: JsonrpcService, private jsonrpc: JsonrpcService,
) { } private messageService: MessageService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.getOrders() this.getOrders();
} }
async getOrders(){ async getOrders() {
const purchases: Purchase[] = (await lastValueFrom( const getPurchases = await lastValueFrom(
this.jsonrpc.rpc( this.jsonrpc.rpc(
{ {
method: 'GetAccountPurchase', method: 'GetAccountPurchase',
params: [] params: [],
}, },
RpcService.bonusService RpcService.bonusService
)))['Purchases']; )
);
if (!getPurchases) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return
}
const purchases: Purchase[] = getPurchases && getPurchases['Purchases'];
this.purchases = purchases.map<Purchase>((purchase) => { this.purchases = purchases && purchases.map<Purchase>((purchase) => {
const id = purchase.ID.slice(0,36).toLowerCase(); const id = purchase.ID.slice(0, 36).toLowerCase();
// purchase.Transactions = transactions.filter((transaction) => { // purchase.Transactions = transactions.filter((transaction) => {
// const same = transaction.Purchase === id; // const same = transaction.Purchase === id;
// transaction.HasPurchase = same; // transaction.HasPurchase = same;
@ -48,7 +56,7 @@ export class OrdersComponent implements OnInit {
// }); // });
return purchase; return purchase;
}); });
this.purchasesShortArray = this.purchases.slice(0, this.lastViewOrder) this.purchasesShortArray = this.purchases && this.purchases.slice(0, this.lastViewOrder);
this.ordersLoadingStatus = false; this.ordersLoadingStatus = false;
} }

View File

@ -1,10 +1,10 @@
<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%' }"

View File

@ -8,23 +8,25 @@
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;
cursor: pointer;
img { img {
height: 26px; height: 26px;
} }
} }
.copy { .copy {
background-color: #09467f; background-color: var(--main-color);
color: #fff; color: #fff;
border-radius: 8px; border-radius: 8px;
width: calc(100% - 66px); width: calc(100% - 66px);
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
cursor: pointer;
} }
} }
} }

View File

@ -11,7 +11,7 @@ import { environment } from 'src/environments/environment';
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(
@ -21,13 +21,18 @@ export class RefSystemComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
const accountData = (await lastValueFrom( const accountData = (await lastValueFrom(
this.jsonrpc.rpc({ this.jsonrpc
method: 'GetAccounts', .rpc(
params: [] {
method: 'getTokenData',
params: [],
}, },
RpcService.bonusService RpcService.authService,
)))['Cards'][0] true
this.refUrl += accountData.CardNumber )
)).data
this.refUrl += accountData.user_id
this.loading = false this.loading = false
} }
@ -35,7 +40,7 @@ export class RefSystemComponent implements OnInit {
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({
title: document.title, title: document.title,
text: "Fashion Logica", text: "Coffee Like Test",
url: this.refUrl url: this.refUrl
}) })
.then(() => console.log('Successful share')) .then(() => console.log('Successful share'))
@ -48,12 +53,14 @@ export class RefSystemComponent implements OnInit {
copyUrl() { copyUrl() {
navigator.clipboard.writeText(this.refUrl) navigator.clipboard.writeText(this.refUrl)
.then(() => { .then(() => {
this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'custom', severity: 'custom',
summary: 'Ссылка скопирована!', summary: 'Ссылка скопирована!',
}); });
}) })
.catch(err => { .catch(err => {
this.messageService.clear();
this.messageService.add({ this.messageService.add({
severity: 'error', severity: 'error',
summary: 'Произошла ошибка!', summary: 'Произошла ошибка!',

View File

@ -75,12 +75,12 @@
<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>

View File

@ -1,7 +1,9 @@
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 { lastValueFrom } from 'rxjs';
import { UserDataForm } from 'src/app/interface/data'; import { UserDataForm } from 'src/app/interface/data';
import { AppleWalletService } from 'src/app/services/apple-wallet.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service'; import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({ @Component({
@ -21,6 +23,8 @@ export class UserDataComponent implements OnInit {
constructor( constructor(
private jsonRpcService: JsonrpcService, private jsonRpcService: JsonrpcService,
private messageService: MessageService, private messageService: MessageService,
private appleWallet: AppleWalletService,
private jsonrpc: JsonrpcService,
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
@ -30,6 +34,14 @@ export class UserDataComponent implements OnInit {
params: [] params: []
}, RpcService.authService, true).subscribe({ }, RpcService.authService, true).subscribe({
next: (res) => { next: (res) => {
if (res.code === 45) {
this.messageService.add({severity:'warn', summary:'Данные не найдены, заполните их на этой странице'});
return
}
if (!res.data) {
this.messageService.add({severity:'error', summary:'Произошла ошибка, попробуйте позже'});
return
}
const { first_name, birthdate, gender } = res.data const { first_name, birthdate, gender } = res.data
this.userData = { first_name, birthdate, gender } this.userData = { first_name, birthdate, gender }
this.createUserDataForm({...this.userData}) this.createUserDataForm({...this.userData})
@ -59,8 +71,29 @@ export class UserDataComponent implements OnInit {
method: 'updateAdditionalInfo', method: 'updateAdditionalInfo',
params: [this.userData] params: [this.userData]
}, RpcService.authService, true).subscribe({ }, RpcService.authService, true).subscribe({
next: () => { next: async () => {
this.messageService.add({severity:'custom', summary:'Данные успешно обновлены!'}); 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) => { error: (err) => {
console.error('Error: ', err) console.error('Error: ', err)

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();
});
});

View File

@ -0,0 +1,168 @@
import {
AfterViewInit,
Component,
EventEmitter,
HostListener,
OnInit
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { MessageService } from 'primeng/api';
import { CookiesService } from 'src/app/services/cookies.service';
import { JsonrpcService, RpcService } from 'src/app/services/jsonrpc.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit, AfterViewInit {
public isShowNumber: boolean = true;
public phoneForm = new FormGroup({
name: new FormControl('', []),
phone: new FormControl('', [Validators.required]),
});
public codeForm = new FormGroup({
code: new FormControl('', [Validators.required]),
code1: new FormControl('', [Validators.required]),
code2: new FormControl('', [Validators.required]),
code3: new FormControl('', [Validators.required]),
});
private inputIds = ['field', 'field1', 'field2', 'field3'];
timeLeft: number = 0;
constructor(
private cookiesService: CookiesService,
private router: Router,
private jsonrpc: JsonrpcService,
private messageService: MessageService,
private _snackBar: MatSnackBar
) { }
ngOnInit(): void { }
ngAfterViewInit() {
setTimeout(() => {
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
}, 1000)
}
public inputFocusEmitter = new EventEmitter<string>();
@HostListener('window:keyup', ['$event'])
HandlKeyEvents(event: any) {
if (!event.target.classList.contains('field')) return;
const key = event.key.toLocaleLowerCase();
let elementId = '';
switch (key) {
case 'backspace':
elementId = event.target.id;
event.target.value = '';
const prevInputIndex = this.inputIds.indexOf(elementId) - 1;
if (prevInputIndex >= 0) {
this.inputFocusEmitter.emit(`#${this.inputIds[prevInputIndex]}`);
}
break;
default:
elementId = event.target.id;
const index = this.inputIds.indexOf(elementId);
const nextInputIndex = index + 1;
if (event.target.value.length > 1) {
event.target.value = event.target.value.slice(-1);
}
if (nextInputIndex > 0 && nextInputIndex <= this.inputIds.length) {
this.inputFocusEmitter.emit(`#${this.inputIds[nextInputIndex]}`);
}
break;
}
}
submitNumber() {
const data = this.phoneForm.value;
this.isShowNumber = false;
if (this.timeLeft) {
this.messageService.clear();
this.messageService.add({
severity: 'custom',
summary: `Отправить повторно можно через ${this.timeLeft}с`,
});
return;
}
this.jsonrpc.rpc({
method: 'sendVerifyByPhone',
params: [data.phone]
}, RpcService.authService, false).subscribe({
next: (result) => {
if (result.code !== 0) {
this._snackBar.open('Произошла ошибка! Попробуйте позже', '', {
duration: 3000
})
}
if (result.code === 0) {
this.timeLeft = 60;
const interval = setInterval(() => {
if (this.timeLeft > 0) {
this.timeLeft--;
} else {
clearInterval(interval);
}
}, 1000)
}
this.isShowNumber = false;
},
error: (error) => {
console.error('Error: ', error);
}
}
);
setTimeout(() => {
this.inputFocusEmitter.emit(`#${this.inputIds[0]}`);
}, 0)
}
submitCode() {
const data = this.codeForm.value;
this.jsonrpc.rpc({
method: 'getTokenByPhone',
params: [this.phoneForm.value.phone, Object.values(data).join('')]
}, RpcService.authService, false).subscribe({
next: (result) => {
if (result.code === 0) {
this.cookiesService.setCookie('token', result?.data?.token);
this.router.navigate(['/'], {
queryParams: {
token: result?.data?.token
},
});
// this.phoneConfirmed.emit(null);
} else if (result.code === 230) {
this._snackBar.open('Неверный код!', '', {
duration: 3000
})
// this.errorConfirmCode = true;
}
},
error: (error) => {
console.error(error);
}
}
);
}
backToPhoneForm() {
this.codeForm.setValue({
code: '',
code1: '',
code2: '',
code3: ''
})
this.isShowNumber = true
}
}

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