Squashed commit of the following:
commit16d8b87e90Merge:07387faf486bea232d47Author: negue <eugen.bolz@gmail.com> Date: Thu Sep 14 22:30:00 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit07387faf48Author: negue <eugen.bolz@gmail.com> Date: Wed Sep 13 23:38:37 2023 +0200 remove generate promoCode from ui commit6bea232d47Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Sep 11 12:55:31 2023 -0400 build(deps): bump core-js from 3.32.1 to 3.32.2 in /website/client (#14867) Bumps [core-js](https://github.com/zloirock/core-js/tree/HEAD/packages/core-js) from 3.32.1 to 3.32.2. - [Release notes](https://github.com/zloirock/core-js/releases) - [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/zloirock/core-js/commits/v3.32.2/packages/core-js) --- updated-dependencies: - dependency-name: core-js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitcebb3f0f25Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Sep 11 12:43:49 2023 -0400 build(deps): bump webpack from 4.46.0 to 4.47.0 in /website/client (#14868) Bumps [webpack](https://github.com/webpack/webpack) from 4.46.0 to 4.47.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v4.46.0...v4.47.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitea8563cd17Merge:3e16584dcf6259955891Author: negue <eugen.bolz@gmail.com> Date: Tue Aug 29 21:23:02 2023 +0200 Merge remote-tracking branch 'origin/negue/ui/setting' into negue/ui/setting commit3e16584dcfAuthor: negue <eugen.bolz@gmail.com> Date: Tue Aug 29 21:22:06 2023 +0200 fix PR comments commit84ba44fb19Author: negue <eugen.bolz@gmail.com> Date: Tue Aug 29 20:38:54 2023 +0200 fix PR comments commit6259955891Author: CuriousMagpie <eilatan@gmail.com> Date: Fri Aug 25 11:20:26 2023 -0400 update form.scss commitda82bd8e68Author: negue <eugen.bolz@gmail.com> Date: Thu Aug 24 21:40:02 2023 +0200 remove ending commit82e5fd2a83Author: negue <eugen.bolz@gmail.com> Date: Mon Aug 21 22:25:41 2023 +0200 fix spacing commit9ad06ea88bAuthor: negue <eugen.bolz@gmail.com> Date: Mon Aug 21 22:09:22 2023 +0200 clean up debug row for login methods commit41cde37675Merge:8c568060f982ebe71eb4Author: negue <eugen.bolz@gmail.com> Date: Mon Aug 21 21:51:22 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit8c568060f9Author: negue <eugen.bolz@gmail.com> Date: Mon Aug 21 21:49:31 2023 +0200 fix PR comments commit36f7a4711dMerge:d279af7897647b27c55fAuthor: negue <eugen.bolz@gmail.com> Date: Fri Aug 11 20:04:15 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitd279af7897Merge:ffbed3e044b20ea44d49Author: negue <eugen.bolz@gmail.com> Date: Wed Aug 9 21:13:37 2023 +0200 Merge branch 'negue/refactor/routes' into negue/ui/setting commitb20ea44d49Author: negue <eugen.bolz@gmail.com> Date: Wed Aug 9 21:04:12 2023 +0200 Split Vue.Router routes commitffbed3e044Author: negue <eugen.bolz@gmail.com> Date: Sun Jul 23 00:00:24 2023 +0200 remove console commit4c350b0180Author: negue <eugen.bolz@gmail.com> Date: Sat Jul 22 23:34:20 2023 +0200 update Bailey Notification Text + fix popover commitc105b9ecf9Author: negue <eugen.bolz@gmail.com> Date: Sat Jul 22 23:21:53 2023 +0200 fix change password setting commit06410b4807Author: negue <eugen.bolz@gmail.com> Date: Sat Jul 22 22:50:00 2023 +0200 fix reset account texts commitccfdd9bb9cMerge:35c75304f18558dcc3a8Author: negue <eugen.bolz@gmail.com> Date: Sat Jul 22 22:48:13 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit35c75304f1Author: negue <eugen.bolz@gmail.com> Date: Sun Jul 2 20:16:06 2023 +0200 more fixes commit203e961464Author: negue <eugen.bolz@gmail.com> Date: Sun Jul 2 19:45:17 2023 +0200 fix notification settings commitec94604791Author: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 22:00:45 2023 +0200 applied same styling to promoCode.vue commit0177b3a76bAuthor: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 21:41:05 2023 +0200 move promoCode.vue to pages/settings commit8fbb600273Author: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 21:40:35 2023 +0200 saveCancelButtons.vue allow to hide the cancel part commit4915f2a3fbAuthor: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 21:09:07 2023 +0200 Hide Transactions Page again commit8b5ae17f02Author: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 20:52:03 2023 +0200 also check for invalid arguments in the password settings commitaa97ed5299Author: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 20:25:53 2023 +0200 fix localhost externalLinks check commit87a4e4931bAuthor: negue <eugen.bolz@gmail.com> Date: Sun Jun 25 20:01:31 2023 +0200 show notification on username change + fix userEmail checks commit6a6f55f6fcMerge:f9ff5e5c55e49d26eacdAuthor: negue <eugen.bolz@gmail.com> Date: Sat Jun 24 22:54:00 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitf9ff5e5c55Author: negue <eugen.bolz@gmail.com> Date: Tue May 30 22:41:42 2023 +0200 check password inputs and mark invalid for "password change" setting commit4497514eebAuthor: negue <eugen.bolz@gmail.com> Date: Tue May 30 21:59:21 2023 +0200 show notification when chaning display name commit3232f12f0dAuthor: negue <eugen.bolz@gmail.com> Date: Tue May 30 21:55:25 2023 +0200 check current password valid style in "delete account" and "reset account" commit582a2f1304Author: negue <eugen.bolz@gmail.com> Date: Tue May 30 21:27:20 2023 +0200 mark password field of email setting as invalid on wrong password commit8e3b8a962aAuthor: negue <eugen.bolz@gmail.com> Date: Tue May 30 21:24:46 2023 +0200 refactor currentPasswordInput.vue to use validatedTextInput.vue commit61521507a4Author: negue <eugen.bolz@gmail.com> Date: Tue May 30 20:20:56 2023 +0200 fix username setting: - unsaved values check - @ char must be first in input, otherwise not remove it for checks commitf74c29a065Merge:c4b6f0c39cd4a5823916Author: negue <eugen.bolz@gmail.com> Date: Tue May 30 19:54:06 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitc4b6f0c39cMerge:37eee140ad6e3a367832Author: negue <eugen.bolz@gmail.com> Date: Fri May 12 22:08:08 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit37eee140adAuthor: negue <eugen.bolz@gmail.com> Date: Fri May 12 21:57:27 2023 +0200 delete account without password commit48a6801f4eAuthor: negue <eugen.bolz@gmail.com> Date: Mon May 8 22:06:29 2023 +0200 fix duplicate json entry commit47a2189f49Merge:a56b4a445749f45d27e3Author: negue <eugen.bolz@gmail.com> Date: Mon May 8 21:48:21 2023 +0200 Merge remote-tracking branch 'origin/release' into negue/ui/setting commita56b4a4457Author: negue <eugen.bolz@gmail.com> Date: Mon May 8 21:37:31 2023 +0200 show current class on setting panel commit9c973cca2aAuthor: negue <eugen.bolz@gmail.com> Date: Mon May 8 21:15:46 2023 +0200 fix selectDifficulty.vue - refactor selectList.vue commit95b37b3ba3Author: negue <eugen.bolz@gmail.com> Date: Mon May 8 20:45:09 2023 +0200 migrate restoreValues fix to new setting component commit7947b1c67dMerge:ad3e4d604a71e165433aAuthor: negue <eugen.bolz@gmail.com> Date: Mon May 8 20:41:31 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitad3e4d604aAuthor: negue <eugen.bolz@gmail.com> Date: Sat Apr 29 01:18:25 2023 +0200 style fixes commitcea13d5bc3Merge:73a5e5fcabb159182188Author: negue <eugen.bolz@gmail.com> Date: Fri Apr 28 23:58:09 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit73a5e5fcabAuthor: negue <eugen.bolz@gmail.com> Date: Tue Apr 25 20:51:14 2023 +0200 style / padding issues commit0a10eb32ccAuthor: negue <eugen.bolz@gmail.com> Date: Sat Apr 15 20:54:08 2023 +0200 fix "setting new password" invalid check commita79bec3fa5Author: negue <eugen.bolz@gmail.com> Date: Tue Apr 11 23:15:15 2023 +0200 add password for other logins commit9ff17fd6ddAuthor: negue <eugen.bolz@gmail.com> Date: Tue Apr 11 23:05:19 2023 +0200 "fix values" use keydown event to mark as change commit1f470942a9Author: negue <eugen.bolz@gmail.com> Date: Thu Apr 6 00:19:18 2023 +0200 delete old api.vue commitb4904a8b84Merge:b5da7ccc70c8b98678d0Author: negue <eugen.bolz@gmail.com> Date: Thu Apr 6 00:18:07 2023 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitb5da7ccc70Author: negue <eugen.bolz@gmail.com> Date: Thu Apr 6 00:11:36 2023 +0200 refactor webhook ui to use save/cancel buttons commitf49f67ff5cAuthor: negue <eugen.bolz@gmail.com> Date: Wed Apr 5 22:56:37 2023 +0200 remove unused settings commitcc73b44b25Author: negue <eugen.bolz@gmail.com> Date: Wed Mar 29 23:40:30 2023 +0200 remove advancedCollapsed settings to start it opened commite0300e8710Author: negue <eugen.bolz@gmail.com> Date: Wed Mar 29 22:58:09 2023 +0200 remove displayInviteToPartyWhenPartyIs1 setting commit1741ddfc64Author: negue <eugen.bolz@gmail.com> Date: Mon Mar 20 23:00:17 2023 +0100 webhook margins commit24a43d027cAuthor: negue <eugen.bolz@gmail.com> Date: Mon Mar 20 22:40:19 2023 +0100 userid tooltip commit42fcb20bc4Author: negue <eugen.bolz@gmail.com> Date: Thu Mar 16 00:51:10 2023 +0100 remove balance for choosing class commit160848473dAuthor: negue <eugen.bolz@gmail.com> Date: Thu Mar 16 00:20:56 2023 +0100 show real class setting modal if enough gems available commitf74ba9738dAuthor: negue <eugen.bolz@gmail.com> Date: Thu Mar 16 00:10:53 2023 +0100 update apple icon and size commitbf961bc728Author: negue <eugen.bolz@gmail.com> Date: Wed Mar 15 23:59:42 2023 +0100 Copied API Token Notification commit28f0220b4eAuthor: negue <eugen.bolz@gmail.com> Date: Wed Mar 15 23:53:33 2023 +0100 remove blue color of setting links commitb53ccace95Author: negue <eugen.bolz@gmail.com> Date: Wed Mar 15 23:43:06 2023 +0100 fix username/email setting input width commit1dfa5b275dAuthor: negue <eugen.bolz@gmail.com> Date: Wed Mar 15 23:11:32 2023 +0100 developer mode commit776618d2dbAuthor: negue <eugen.bolz@gmail.com> Date: Tue Mar 14 21:11:52 2023 +0100 Add new Pause Dailies Setting commit576c80af7eMerge:dec1a1159d377b152ffdAuthor: negue <eugen.bolz@gmail.com> Date: Tue Mar 14 21:04:05 2023 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitdec1a1159dAuthor: negue <eugen.bolz@gmail.com> Date: Tue Mar 14 21:00:52 2023 +0100 developer mode dummy row commit1e80a7d145Author: negue <eugen.bolz@gmail.com> Date: Sat Mar 11 00:03:33 2023 +0100 WIP webhook row commitcc4bedbe2dAuthor: negue <eugen.bolz@gmail.com> Date: Fri Mar 10 20:28:57 2023 +0100 add spritely login creds message to the new api-row / redirect old url to the new one commitf9833aa78aAuthor: negue <eugen.bolz@gmail.com> Date: Thu Mar 9 02:23:39 2023 +0100 API Token Row commit123c9b9bb1Author: negue <eugen.bolz@gmail.com> Date: Mon Mar 6 22:46:50 2023 +0100 "Your User Data" Row instead of Page commit0ade5663aeAuthor: negue <eugen.bolz@gmail.com> Date: Fri Mar 3 22:43:03 2023 +0100 userid row commitb4f2236ab8Author: negue <eugen.bolz@gmail.com> Date: Fri Mar 3 22:22:32 2023 +0100 rename folder of setting rows commit3b050861c4Author: negue <eugen.bolz@gmail.com> Date: Tue Feb 21 21:11:48 2023 +0100 move remaining setting to generalSettings.vue - delete site.vue - start with siteData.vue commitb09298fb01Author: negue <eugen.bolz@gmail.com> Date: Tue Feb 21 20:56:03 2023 +0100 move taskSettings.vue and add it to the settings list commit5ed25066ecAuthor: negue <eugen.bolz@gmail.com> Date: Tue Feb 21 20:06:13 2023 +0100 size/margin for transactions commit25e77cbd95Author: negue <eugen.bolz@gmail.com> Date: Tue Feb 21 19:52:12 2023 +0100 move purchaseHistory.vue commit8e4e1bcb0fMerge:bb14d09aa485c50d50e9Author: negue <eugen.bolz@gmail.com> Date: Tue Feb 21 19:04:31 2023 +0100 Merge remote-tracking branch 'origin/negue/ui/setting' into negue/ui/setting commit85c50d50e9Author: SabreCat <sabe@habitica.com> Date: Thu Feb 16 14:23:27 2023 -0600 fix(css): remove redundant formatting for a elements commitbb14d09aa4Author: negue <eugen.bolz@gmail.com> Date: Thu Feb 16 01:34:09 2023 +0100 remove console commit8c5e722c72Author: negue <eugen.bolz@gmail.com> Date: Thu Feb 16 01:26:43 2023 +0100 first try with the refactored UI of Login Methods commit9c8770051dAuthor: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 19:13:16 2023 +0100 fix dayStartAdjustmentSetting.vue for 0 value commitee2ff3881bAuthor: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 18:37:46 2023 +0100 fix color after refactor commit121e7485caAuthor: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 18:29:00 2023 +0100 mark audioThemeSetting as changed commit98c6570003Author: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 18:05:55 2023 +0100 fix ul/li style in resetAccount.vue commitfed824f705Author: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 17:49:36 2023 +0100 fix color of gem price commit80365e537dAuthor: negue <eugen.bolz@gmail.com> Date: Sat Feb 11 17:44:55 2023 +0100 fix "fixValuesSetting.vue" commitd3e15c5413Author: negue <eugen.bolz@gmail.com> Date: Wed Feb 8 01:06:27 2023 +0100 open forgot password in new tab commit31edec9ec5Author: negue <eugen.bolz@gmail.com> Date: Wed Feb 8 01:03:19 2023 +0100 move validatedTextInput.vue to shared components + fix check pos/size + input-error cleanup commit2adfd8c259Author: negue <eugen.bolz@gmail.com> Date: Sun Feb 5 20:19:30 2023 +0100 hide class setting until level 10 commit64fb4c0cf9Author: negue <eugen.bolz@gmail.com> Date: Sun Feb 5 19:32:40 2023 +0100 delete old modals (refactored into new settings ui) commitb5be137a8dAuthor: negue <eugen.bolz@gmail.com> Date: Sun Feb 5 19:27:26 2023 +0100 enable forgot password link in settings commitbec75c6e12Author: negue <eugen.bolz@gmail.com> Date: Sun Feb 5 18:52:54 2023 +0100 reset account + password required in api commit64f7e7a1d9Author: negue <eugen.bolz@gmail.com> Date: Mon Jan 30 23:22:55 2023 +0100 fix compile commit7ffb5101beMerge:2bfb130b929f64633a57Author: negue <eugen.bolz@gmail.com> Date: Mon Jan 30 22:47:05 2023 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit2bfb130b92Author: negue <eugen.bolz@gmail.com> Date: Mon Jan 30 22:44:23 2023 +0100 remove restore-modal and replace it with the finished fix values setting commit89530a133cAuthor: negue <eugen.bolz@gmail.com> Date: Wed Jan 18 19:22:36 2023 +0100 wip fix values commit428647fc71Author: negue <eugen.bolz@gmail.com> Date: Sat Jan 14 21:50:22 2023 +0100 refactor change class to design update + clean up old site.vue settings commit1f16819bc1Author: negue <eugen.bolz@gmail.com> Date: Wed Jan 11 22:41:05 2023 +0100 WIP fix values commit6fef3d0579Author: negue <eugen.bolz@gmail.com> Date: Sat Jan 7 22:51:30 2023 +0100 check for unsaved changes when pressing cancel commitbef8a4cdfcMerge:494f32c3e3c7aadede4dAuthor: negue <eugen.bolz@gmail.com> Date: Sat Jan 7 22:10:53 2023 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit494f32c3e3Author: negue <eugen.bolz@gmail.com> Date: Wed Dec 21 00:55:31 2022 +0100 Class Setting commitbda210cfbbAuthor: negue <eugen.bolz@gmail.com> Date: Tue Dec 20 23:01:41 2022 +0100 removes username, email and display name from site.vue commit38198d7df6Author: negue <eugen.bolz@gmail.com> Date: Tue Dec 20 22:36:27 2022 +0100 WIP class setting commitdddcfa637fAuthor: negue <eugen.bolz@gmail.com> Date: Tue Dec 20 22:31:36 2022 +0100 fix styles commitce0a5cf974Author: negue <eugen.bolz@gmail.com> Date: Sun Dec 11 23:57:07 2022 +0100 Scroll into opened Setting commit7e0a95ddffAuthor: negue <eugen.bolz@gmail.com> Date: Sun Dec 11 23:43:44 2022 +0100 Audio Theme Setting commit9c556662feAuthor: negue <eugen.bolz@gmail.com> Date: Sun Dec 11 00:25:30 2022 +0100 prepare header settings but still hidden commit30d8b27534Merge:a1d1a788b2580139ff69Author: negue <eugen.bolz@gmail.com> Date: Sat Dec 10 23:36:36 2022 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commita1d1a788b2Author: negue <eugen.bolz@gmail.com> Date: Sat Dec 10 23:34:33 2022 +0100 DayStartAdjustmentSetting commitddee94a393Author: negue <eugen.bolz@gmail.com> Date: Sat Dec 10 20:00:12 2022 +0100 disable reset account button when password empty commit30a6db4c2dAuthor: negue <eugen.bolz@gmail.com> Date: Sat Dec 10 19:54:21 2022 +0100 hide & reset previous setting when switching to a different one commit78093848d7Author: negue <eugen.bolz@gmail.com> Date: Wed Dec 7 22:19:15 2022 +0100 validated text input (in/valid border color + icon) commite1b444ea63Author: negue <eugen.bolz@gmail.com> Date: Tue Dec 6 22:09:54 2022 +0100 re-enable box-shadow on hover commit96dc4e47aeAuthor: negue <negue@users.noreply.github.com> Date: Mon Nov 28 01:13:47 2022 +0100 remove console log commit69ad07daadAuthor: negue <negue@users.noreply.github.com> Date: Mon Nov 28 01:01:17 2022 +0100 dateFormatSetting commitbc11c0cf75Author: negue <negue@users.noreply.github.com> Date: Mon Nov 28 00:49:24 2022 +0100 move shared components / mixins commit0d1a189c64Author: negue <negue@users.noreply.github.com> Date: Mon Nov 28 00:44:21 2022 +0100 language Setting + imports cleanup commit29ebd89030Author: negue <negue@users.noreply.github.com> Date: Sun Nov 27 23:23:02 2022 +0100 fix icon size + fix display name valid checks commit5c7747517bMerge:fd5cbc302690b34c4dacAuthor: negue <negue@users.noreply.github.com> Date: Sun Nov 27 23:08:35 2022 +0100 Merge remote-tracking branch 'origin/release' into negue/ui/setting commitfd5cbc3026Author: negue <negue@users.noreply.github.com> Date: Wed Nov 23 00:14:21 2022 +0100 fix conflicts commit49361217b0Merge:edb427158f04e2a39a9fAuthor: negue <negue@users.noreply.github.com> Date: Wed Nov 23 00:12:38 2022 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitedb427158fAuthor: negue <negue@users.noreply.github.com> Date: Wed Nov 23 00:03:19 2022 +0100 disable save button if nothing was changed commitc7e40e9446Author: negue <negue@users.noreply.github.com> Date: Tue Nov 22 23:36:37 2022 +0100 delete account row commit4bf740c531Author: negue <negue@users.noreply.github.com> Date: Tue Nov 22 23:14:24 2022 +0100 Shared Modal Visible State commitd718153717Author: negue <negue@users.noreply.github.com> Date: Sun Nov 20 18:06:20 2022 +0100 resetAccount commite25922f8b3Author: negue <negue@users.noreply.github.com> Date: Wed Nov 16 23:39:26 2022 +0100 rename functional components for compiler commitfdbc2c0eeeAuthor: negue <negue@users.noreply.github.com> Date: Wed Nov 16 01:44:50 2022 +0100 password setting row commit5fd5e6275aAuthor: negue <negue@users.noreply.github.com> Date: Tue Nov 15 17:35:44 2022 +0100 update package-lock.json again commit9d742fd9a1Author: negue <negue@users.noreply.github.com> Date: Tue Nov 15 17:24:15 2022 +0100 update package-lock.json commitcd588e74d5Author: negue <negue@users.noreply.github.com> Date: Mon Nov 14 02:12:39 2022 +0100 displayNameSetting.vue commit265970c5efAuthor: negue <negue@users.noreply.github.com> Date: Mon Nov 14 02:09:47 2022 +0100 fix lint commita2b510cacaMerge:0bae5fbe024dca69f14bAuthor: negue <negue@users.noreply.github.com> Date: Mon Nov 14 01:15:02 2022 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit0bae5fbe02Author: negue <negue@users.noreply.github.com> Date: Sun Nov 13 22:00:34 2022 +0100 userEmailSetting commit23da70fa2eAuthor: negue <negue@users.noreply.github.com> Date: Sun Nov 13 20:38:14 2022 +0100 extract save / cancel buttons and the shared inlineSetting "logic" commit82047380f3Author: negue <negue@users.noreply.github.com> Date: Sun Nov 13 20:18:21 2022 +0100 first setting (username) in the new layout commit39150349c7Author: negue <negue@users.noreply.github.com> Date: Wed Nov 2 21:42:12 2022 +0100 Working on M1 - will be reverted on full merge commitf7787b318cMerge:4c0ecc993853fb28cc48Author: negue <negue@users.noreply.github.com> Date: Tue Nov 1 14:20:24 2022 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit4c0ecc9938Merge:2f53613a4562b4315b3dAuthor: negue <negue@users.noreply.github.com> Date: Sun Oct 30 12:49:34 2022 +0100 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commit2f53613a45Author: negue <eugen.bolz@gmail.com> Date: Mon Oct 10 22:54:41 2022 +0200 split routes for ease of dev commit390f0fc69dMerge:cf222ee63a137f7d53dcAuthor: negue <eugen.bolz@gmail.com> Date: Mon Oct 10 22:50:43 2022 +0200 Merge remote-tracking branch 'origin/develop' into negue/ui/setting commitcf222ee63aAuthor: negue <eugen.bolz@gmail.com> Date: Sun Oct 2 23:15:35 2022 +0200 Update remaining Notification labels commitf837cce125Author: negue <eugen.bolz@gmail.com> Date: Sun Oct 2 22:45:12 2022 +0200 move site popup settings to notifications commitfc5181c3a7Author: negue <eugen.bolz@gmail.com> Date: Sun Oct 2 21:12:24 2022 +0200 fix styling in notification settings commit7b5568ed23Author: negue <eugen.bolz@gmail.com> Date: Sat Sep 10 16:00:56 2022 +0200 wip notification settings
This commit is contained in:
@@ -731,6 +731,8 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.forgotPassword = this.$route.path.startsWith('/forgot-password');
|
||||
|
||||
hello.init({
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
|
||||
@@ -245,12 +245,13 @@ import notifications from '@/mixins/notifications';
|
||||
import closeX from '../ui/closeX';
|
||||
|
||||
import copyIcon from '@/assets/svg/copy.svg';
|
||||
import copyToClipboard from '@/mixins/copyToClipboard';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
closeX,
|
||||
},
|
||||
mixins: [notifications],
|
||||
mixins: [notifications, copyToClipboard],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
@@ -287,17 +288,10 @@ export default {
|
||||
this.$root.$emit('bv::hide::modal', 'create-party-modal');
|
||||
},
|
||||
copyUsername () {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(this.user.auth.local.username);
|
||||
} else {
|
||||
const copyText = document.createElement('textarea');
|
||||
copyText.value = this.user.auth.local.username;
|
||||
document.body.appendChild(copyText);
|
||||
copyText.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(copyText);
|
||||
}
|
||||
this.text(this.$t('usernameCopied'));
|
||||
this.mixinCopyToClipboard(
|
||||
this.user.auth.local.username,
|
||||
this.$t('usernameCopied'),
|
||||
);
|
||||
},
|
||||
seekParty () {
|
||||
this.$store.dispatch('user:set', {
|
||||
|
||||
@@ -86,11 +86,6 @@
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
color: $red-50;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
border-radius: 2px;
|
||||
border: solid 1px $gray-400;
|
||||
|
||||
@@ -117,14 +117,14 @@ import * as quests from '@/../../common/script/content/quests';
|
||||
import { hasCompletedOnboarding } from '@/../../common/script/libs/onboarding';
|
||||
import notificationsIcon from '@/assets/svg/notifications.svg';
|
||||
import MenuDropdown from '../ui/customMenuDropdown';
|
||||
import MessageCount from './messageCount';
|
||||
import MessageCount from './messageCount.functional.vue';
|
||||
import { CONSTANTS, getLocalSetting, setLocalSetting } from '@/libs/userlocalManager';
|
||||
import successImage from '@/assets/svg/success.svg';
|
||||
import starBadge from '@/assets/svg/star-badge.svg';
|
||||
|
||||
// Notifications
|
||||
import CARD_RECEIVED from './notifications/cardReceived';
|
||||
import CHALLENGE_INVITATION from './notifications/challengeInvitation';
|
||||
import CHALLENGE_INVITATION from './notifications/challengeInvitation.functional.vue';
|
||||
import GIFT_ONE_GET_ONE from './notifications/g1g1';
|
||||
import GROUP_TASK_ASSIGNED from './notifications/groupTaskAssigned';
|
||||
import GROUP_TASK_CLAIMED from './notifications/groupTaskClaimed';
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
>{{ $t('achievements') }}</a>
|
||||
<router-link
|
||||
class="topbar-dropdown-item dropdown-item"
|
||||
:to="{name: 'site'}"
|
||||
:to="{name: 'general'}"
|
||||
>
|
||||
{{ $t('settings') }}
|
||||
</router-link>
|
||||
@@ -141,7 +141,7 @@
|
||||
import { mapState } from '@/libs/store';
|
||||
import userIcon from '@/assets/svg/user.svg';
|
||||
import MenuDropdown from '../ui/customMenuDropdown';
|
||||
import MessageCount from './messageCount';
|
||||
import MessageCount from './messageCount.functional.vue';
|
||||
import { EVENTS } from '@/libs/events';
|
||||
import { PAGES } from '@/libs/consts';
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
<div v-if="currentDraggingEgg != null">
|
||||
<div
|
||||
class="potion-icon"
|
||||
:class="'Pet_Egg_'+currentDraggingEgg.key"
|
||||
:class="`Pet_Egg_${currentDraggingEgg.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div class="popover-content">
|
||||
@@ -248,7 +248,7 @@
|
||||
<div v-if="currentDraggingEgg != null">
|
||||
<div
|
||||
class="potion-icon"
|
||||
:class="'Pet_Egg_'+currentDraggingEgg.key"
|
||||
:class="`Pet_Egg_${currentDraggingEgg.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div
|
||||
@@ -266,7 +266,7 @@
|
||||
<div v-if="currentDraggingPotion != null">
|
||||
<div
|
||||
class="potion-icon"
|
||||
:class="'Pet_HatchingPotion_'+currentDraggingPotion.key"
|
||||
:class="`Pet_HatchingPotion_${currentDraggingPotion.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div
|
||||
@@ -285,7 +285,7 @@
|
||||
<div v-if="currentDraggingPotion != null">
|
||||
<div
|
||||
class="potion-icon"
|
||||
:class="'Pet_HatchingPotion_'+currentDraggingPotion.key"
|
||||
:class="`Pet_HatchingPotion_${currentDraggingPotion.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<span
|
||||
v-drag.food="item.key"
|
||||
class="item-content"
|
||||
:class="'Pet_Food_'+item.key"
|
||||
:class="`Pet_Food_${item.key}`"
|
||||
@itemDragEnd="dragend($event)"
|
||||
@itemDragStart="dragstart($event)"
|
||||
></span>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
>
|
||||
<div class="potionEggGroup">
|
||||
<div class="potionEggBackground">
|
||||
<div :class="'Pet_HatchingPotion_'+hatchablePet.potionKey"></div>
|
||||
<div :class="`Pet_HatchingPotion_${hatchablePet.potionKey}`"></div>
|
||||
</div>
|
||||
<div class="potionEggBackground">
|
||||
<div :class="'Pet_Egg_'+hatchablePet.eggKey"></div>
|
||||
<div :class="`Pet_Egg_${hatchablePet.eggKey}`"></div>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="title">
|
||||
|
||||
@@ -268,7 +268,7 @@
|
||||
<div v-if="currentDraggingFood != null">
|
||||
<div
|
||||
class="food-icon"
|
||||
:class="'Pet_Food_'+currentDraggingFood.key"
|
||||
:class="`Pet_Food_${currentDraggingFood.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div
|
||||
@@ -287,7 +287,7 @@
|
||||
<div v-if="currentDraggingFood != null">
|
||||
<div
|
||||
class="food-icon"
|
||||
:class="'Pet_Food_'+currentDraggingFood.key"
|
||||
:class="`Pet_Food_${currentDraggingFood.key}`"
|
||||
></div>
|
||||
<div class="popover">
|
||||
<div
|
||||
|
||||
@@ -22,18 +22,19 @@
|
||||
v-if="currentEvent && currentEvent.promo === 'g1g1'"
|
||||
class="g1g1-margin d-flex flex-column align-items-center"
|
||||
>
|
||||
<div
|
||||
class="svg-big-gift"
|
||||
v-once
|
||||
v-html="icons.bigGift"
|
||||
></div>
|
||||
<div
|
||||
v-once
|
||||
class="svg-big-gift"
|
||||
v-html="icons.bigGift"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="d-flex flex-column align-items-center">
|
||||
v-else
|
||||
class="d-flex flex-column align-items-center"
|
||||
>
|
||||
<div
|
||||
class="svg-big-gift"
|
||||
v-once
|
||||
class="svg-big-gift"
|
||||
v-html="icons.bigGift"
|
||||
></div>
|
||||
</div>
|
||||
@@ -49,9 +50,10 @@
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="modal-close"
|
||||
@click="close()">
|
||||
v-else
|
||||
class="modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
v-html="icons.close"
|
||||
@@ -65,26 +67,15 @@
|
||||
name="selectUser"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div class="input-group">
|
||||
<input
|
||||
id="selectUser"
|
||||
v-model="userSearchTerm"
|
||||
class="form-control"
|
||||
type="text"
|
||||
ref="textBox"
|
||||
:placeholder="$t('usernameOrUserId')"
|
||||
:class="{
|
||||
'input-valid': foundUser._id,
|
||||
'is-invalid input-invalid': userNotFound,
|
||||
}"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="userSearchTerm.length > 0 && userNotFound"
|
||||
class="input-error text-center mt-2"
|
||||
>
|
||||
{{ $t('userWithUsernameOrUserIdNotFound') }}
|
||||
</div>
|
||||
<validated-text-input
|
||||
id="selectUser"
|
||||
v-model="userSearchTerm"
|
||||
:is-valid="foundUser._id"
|
||||
|
||||
:placeholder="$t('usernameOrUserId')"
|
||||
:invalid-issues="userInputInvalidIssues"
|
||||
/>
|
||||
|
||||
<div class="d-flex flex-column justify-content-center align-items-middle mt-3">
|
||||
<button
|
||||
class="btn btn-primary mx-auto mt-2"
|
||||
@@ -104,16 +95,12 @@
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
v-if="currentEvent && currentEvent.promo ==='g1g1'"
|
||||
class="g1g1-cancel d-flex justify-content-center"
|
||||
v-html="$t('cancel')"
|
||||
@click="close()"
|
||||
v-if="currentEvent && currentEvent.promo ==='g1g1'"
|
||||
class="g1g1-cancel d-flex justify-content-center"
|
||||
@click="close()"
|
||||
v-html="$t('cancel')"
|
||||
>
|
||||
{{ $t('cancel') }}
|
||||
</div>
|
||||
<div
|
||||
v-else>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,182 +108,179 @@
|
||||
slot="modal-footer"
|
||||
class="g1g1-fine-print text-center pt-3"
|
||||
>
|
||||
<strong>
|
||||
{{ $t ('howItWorks') }}
|
||||
<strong v-once>
|
||||
{{ $t('howItWorks') }}
|
||||
</strong>
|
||||
<p
|
||||
v-once
|
||||
class="mx-5 mt-1"
|
||||
>
|
||||
{{ $t ('g1g1HowItWorks') }}
|
||||
{{ $t('g1g1HowItWorks') }}
|
||||
</p>
|
||||
<strong>
|
||||
{{ $t ('limitations') }}
|
||||
<strong v-once>
|
||||
{{ $t('limitations') }}
|
||||
</strong>
|
||||
<p
|
||||
v-once
|
||||
class="mx-5 mt-1"
|
||||
>
|
||||
{{ $t ('g1g1Limitations') }}
|
||||
{{ $t('g1g1Limitations') }}
|
||||
</p>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@/assets/scss/mixins.scss';
|
||||
@import '~@/assets/scss/mixins.scss';
|
||||
|
||||
#select-user-modal {
|
||||
.modal-content {
|
||||
width:448px;
|
||||
#select-user-modal {
|
||||
.modal-content {
|
||||
width: 448px;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-top: 0rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
width: 448px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0rem;
|
||||
|
||||
> * {
|
||||
margin: 0rem 0.25rem 0.25rem 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-top: 0rem;
|
||||
}
|
||||
body.modal-open .modal {
|
||||
display: flex !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
width: 448px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0rem;
|
||||
|
||||
> * {
|
||||
margin: 0rem 0.25rem 0.25rem 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
body.modal-open .modal {
|
||||
display: flex !important;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body.modal-open .modal .modal-dialog {
|
||||
margin: auto;
|
||||
}
|
||||
body.modal-open .modal .modal-dialog {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
a:not([href]) {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
}
|
||||
a:not([href]) {
|
||||
|
||||
#selectUser {
|
||||
width: 22rem;
|
||||
border: 0px;
|
||||
color: $gray-50;
|
||||
}
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.71;
|
||||
}
|
||||
|
||||
.g1g1 {
|
||||
background-image: url('~@/assets/images/g1g1-send.png');
|
||||
background-size: 446px 152px;
|
||||
width: 446px;
|
||||
height: 152px;
|
||||
margin: -16px 0px 0px -16px;
|
||||
border-radius: 4.8px 4.8px 0px 0px;
|
||||
padding: 24px;
|
||||
#selectUser {
|
||||
width: 22rem;
|
||||
border: 0px;
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.g1g1 {
|
||||
background-image: url('~@/assets/images/g1g1-send.png');
|
||||
background-size: 446px 152px;
|
||||
width: 446px;
|
||||
height: 152px;
|
||||
margin: -16px 0px 0px -16px;
|
||||
border-radius: 4.8px 4.8px 0px 0px;
|
||||
padding: 24px;
|
||||
color: $white;
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
color: $white;
|
||||
|
||||
h1 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
margin-left: 4rem;
|
||||
margin-right: 4rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
.g1g1-margin {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.g1g1-cancel {
|
||||
margin-top: 16px;
|
||||
color: $blue-10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.g1g1-fine-print {
|
||||
color: $gray-100;
|
||||
background-color: $gray-700;
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
margin-left: 4rem;
|
||||
margin-right: 4rem;
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
.g1g1-modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
.g1g1-margin {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.g1g1-svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
.g1g1-cancel {
|
||||
margin-top: 16px;
|
||||
color: $blue-10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& ::v-deep svg path {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
.g1g1-fine-print {
|
||||
color: $gray-100;
|
||||
background-color: $gray-700;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
.g1g1-modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.g1g1-svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
|
||||
& ::v-deep svg path {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.g1g1-modal-dialog {
|
||||
margin-top: 10vh;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
color: $red-50;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
border-radius: 2px;
|
||||
border: solid 1px $gray-400;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.input-group:focus-within {
|
||||
border-color: $purple-500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
color: $purple-300;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.svg-big-gift {
|
||||
width: 176px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
.g1g1-modal-dialog {
|
||||
margin-top: 10vh;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
border-radius: 2px;
|
||||
border: solid 1px $gray-400;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.input-group:focus-within {
|
||||
border-color: $purple-500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
color: $purple-300;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.svg-big-gift {
|
||||
width: 176px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -308,8 +292,10 @@ import isUUID from 'validator/lib/isUUID';
|
||||
import { mapState } from '@/libs/store';
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
import bigGiftIcon from '@/assets/svg/big-gift.svg';
|
||||
import ValidatedTextInput from '@/components/ui/validatedTextInput.vue';
|
||||
|
||||
export default {
|
||||
components: { ValidatedTextInput },
|
||||
data () {
|
||||
return {
|
||||
userNotFound: false,
|
||||
@@ -332,6 +318,12 @@ export default {
|
||||
if (this.userSearchTerm.length < 1) return true;
|
||||
return typeof this.foundUser._id === 'undefined';
|
||||
},
|
||||
|
||||
userInputInvalidIssues () {
|
||||
return this.userSearchTerm.length > 0 && this.userNotFound
|
||||
? [this.$t('userWithUsernameOrUserIdNotFound')]
|
||||
: [''];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
userSearchTerm: {
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
<template>
|
||||
<div class="row standard-page">
|
||||
<div class="col-6">
|
||||
<h2>{{ $t('API') }}</h2>
|
||||
<p>{{ $t('APIText') }}</p>
|
||||
<div class="section">
|
||||
<h6>{{ $t('userId') }}</h6>
|
||||
<pre class="prettyprint">{{ user.id }}</pre>
|
||||
<h6>{{ $t('APIToken') }}</h6>
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
@click="showApiToken = !showApiToken"
|
||||
>
|
||||
{{ $t(`${showApiToken ? 'hide' : 'show'}APIToken`) }}
|
||||
</button>
|
||||
<pre
|
||||
v-if="showApiToken"
|
||||
class="prettyprint ml-4 mb-0"
|
||||
>{{ apiToken }}</pre>
|
||||
</div>
|
||||
<p v-html="$t('APITokenWarning', { hrefTechAssistanceEmail })"></p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h3>{{ $t('thirdPartyApps') }}</h3>
|
||||
<p v-html="$t('thirdPartyTools')"></p>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2>{{ $t('webhooks') }}</h2>
|
||||
<p v-html="$t('webhooksInfo')"></p>
|
||||
<table class="table table-striped">
|
||||
<thead v-if="user.webhooks.length">
|
||||
<tr>
|
||||
<th>{{ $t('enabled') }}</th>
|
||||
<th>{{ $t('webhookURL') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(webhook, index) in user.webhooks"
|
||||
:key="webhook.id"
|
||||
>
|
||||
<td>
|
||||
<input
|
||||
v-model="webhook.enabled"
|
||||
type="checkbox"
|
||||
@change="saveWebhook(webhook, index)"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="webhook.url"
|
||||
class="form-control"
|
||||
type="url"
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
class="btn btn-danger checklist-icons mr-2"
|
||||
@click="deleteWebhook(webhook, index)"
|
||||
>
|
||||
<span
|
||||
class="glyphicon glyphicon-trash"
|
||||
:tooltip="$t('delete')"
|
||||
> {{ $t('delete') }} </span>
|
||||
</div>
|
||||
<div
|
||||
class="btn btn-primary checklist-icons"
|
||||
@click="saveWebhook(webhook, index)"
|
||||
>
|
||||
{{ $t('subUpdateTitle') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group col-sm-10">
|
||||
<input
|
||||
v-model="newWebhook.url"
|
||||
class="form-control"
|
||||
type="url"
|
||||
:placeholder="$t('webhookURL')"
|
||||
>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button
|
||||
class="btn btn-sm btn-primary"
|
||||
type="submit"
|
||||
@click="addWebhook(newWebhook.url)"
|
||||
>
|
||||
{{ $t('add') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.section {
|
||||
margin-top: 2em;
|
||||
}
|
||||
li span
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import uuid from '@/../../common/script/libs/uuid';
|
||||
// @TODO: env.EMAILS.TECH_ASSISTANCE_EMAIL
|
||||
const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
newWebhook: {
|
||||
url: '',
|
||||
},
|
||||
hrefTechAssistanceEmail: `<a href="mailto:${TECH_ASSISTANCE_EMAIL}">${TECH_ASSISTANCE_EMAIL}</a>`,
|
||||
showApiToken: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data', credentials: 'credentials' }),
|
||||
apiToken () {
|
||||
return this.credentials.API_TOKEN;
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
subSection: this.$t('API'),
|
||||
});
|
||||
window.addEventListener('message', this.receiveMessage, false);
|
||||
},
|
||||
destroy () {
|
||||
window.removeEventListener('message', this.receiveMessage);
|
||||
},
|
||||
methods: {
|
||||
receiveMessage (eventFrom) {
|
||||
if (eventFrom.origin !== 'https://www.spritely.app') return;
|
||||
|
||||
const creds = {
|
||||
userId: this.user._id,
|
||||
apiToken: this.credentials.API_TOKEN,
|
||||
};
|
||||
eventFrom.source.postMessage(creds, eventFrom.origin);
|
||||
},
|
||||
async addWebhook (url) {
|
||||
const webhookInfo = {
|
||||
id: uuid(),
|
||||
type: 'taskActivity',
|
||||
options: {
|
||||
created: false,
|
||||
updated: false,
|
||||
deleted: false,
|
||||
scored: true,
|
||||
},
|
||||
url,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const webhook = await this.$store.dispatch('user:addWebhook', { webhookInfo });
|
||||
this.user.webhooks.push(webhook);
|
||||
|
||||
this.newWebhook.url = '';
|
||||
},
|
||||
async saveWebhook (webhook, index) {
|
||||
delete webhook._editing;
|
||||
const updatedWebhook = await this.$store.dispatch('user:updateWebhook', { webhook });
|
||||
this.user.webhooks[index] = updatedWebhook;
|
||||
},
|
||||
async deleteWebhook (webhook, index) {
|
||||
delete webhook._editing;
|
||||
await this.$store.dispatch('user:deleteWebhook', { webhook });
|
||||
this.user.webhooks.splice(index, 1);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>{{ $t('dataExport') }}</h2>
|
||||
<small>{{ $t('saveData') }}</small>
|
||||
<h4>{{ $t('habitHistory') }}</h4>
|
||||
{{ $t('exportHistory') }}
|
||||
<a href="/export/history.csv">{{ $t('csv') }}</a>
|
||||
<h4>{{ $t('userData') }}</h4>
|
||||
{{ $t('exportUserData') }}
|
||||
<a href="/export/userdata.xml">{{ $t('xml') }}</a>
|
||||
<a href="/export/userdata.json">{{ $t('json') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
subSection: this.$t('dataExport'),
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,132 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<h5>{{ $t('dayStartAdjustment') }}</h5>
|
||||
<div class="mb-4">
|
||||
{{ $t('customDayStartInfo1') }}
|
||||
</div>
|
||||
<h3 v-once>{{ $t('adjustment') }}</h3>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<div class="">
|
||||
<select
|
||||
v-model="newDayStart"
|
||||
class="form-control"
|
||||
>
|
||||
<option
|
||||
v-for="option in dayStartOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
>
|
||||
{{ option.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-primary full-width mt-3"
|
||||
:disabled="newDayStart === user.preferences.dayStart"
|
||||
@click="openDayStartModal()"
|
||||
>
|
||||
{{ $t('save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<small>
|
||||
<p v-html="$t('timezoneUTC', {utc: timezoneOffsetToUtc})"></p>
|
||||
<p v-html="$t('timezoneInfo')"></p>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import moment from 'moment';
|
||||
import getUtcOffset from '../../../../common/script/fns/getUtcOffset';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
name: 'dayStartAdjustment',
|
||||
data () {
|
||||
const dayStartOptions = [];
|
||||
for (let number = 0; number <= 12; number += 1) {
|
||||
const meridian = number < 12 ? 'AM' : 'PM';
|
||||
const hour = number % 12;
|
||||
const timeWithMeridian = `(${hour || 12}:00 ${meridian})`;
|
||||
const option = {
|
||||
value: number,
|
||||
name: `+${number} hours ${timeWithMeridian}`,
|
||||
};
|
||||
|
||||
if (number === 0) {
|
||||
option.name = `Default ${timeWithMeridian}`;
|
||||
}
|
||||
|
||||
dayStartOptions.push(option);
|
||||
}
|
||||
|
||||
return {
|
||||
newDayStart: 0,
|
||||
dayStartOptions,
|
||||
};
|
||||
},
|
||||
mounted () {
|
||||
this.newDayStart = this.user.preferences.dayStart;
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
}),
|
||||
timezoneOffsetToUtc () {
|
||||
const offsetString = moment().utcOffset(getUtcOffset(this.user)).format('Z');
|
||||
return `UTC${offsetString}`;
|
||||
},
|
||||
dayStart () {
|
||||
return this.user.preferences.dayStart;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async saveDayStart () {
|
||||
this.user.preferences.dayStart = this.newDayStart;
|
||||
await axios.post('/api/v4/user/custom-day-start', {
|
||||
dayStart: this.newDayStart,
|
||||
});
|
||||
// @TODO
|
||||
// Notification.text(response.data.data.message);
|
||||
},
|
||||
openDayStartModal () {
|
||||
const nextCron = this.calculateNextCron();
|
||||
// @TODO: Add generic modal
|
||||
if (!window.confirm(this.$t('sureChangeCustomDayStartTime', { time: nextCron }))) return; // eslint-disable-line no-alert
|
||||
this.saveDayStart();
|
||||
// $rootScope.openModal('change-day-start', { scope: $scope });
|
||||
},
|
||||
calculateNextCron () {
|
||||
let nextCron = moment()
|
||||
.hours(this.newDayStart)
|
||||
.minutes(0)
|
||||
.seconds(0)
|
||||
.milliseconds(0);
|
||||
|
||||
const currentHour = moment().format('H');
|
||||
if (currentHour >= this.newDayStart) {
|
||||
nextCron = nextCron.add(1, 'day');
|
||||
}
|
||||
|
||||
return nextCron.format(`${this.user.preferences.dateFormat.toUpperCase()} @ h:mm a`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,86 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="delete"
|
||||
:title="$t('deleteAccount')"
|
||||
:hide-footer="true"
|
||||
size="md"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<br>
|
||||
<strong v-if="user.auth.local.has_password">{{ $t('deleteLocalAccountText') }}</strong>
|
||||
<strong
|
||||
v-if="!user.auth.local.has_password"
|
||||
>{{ $t('deleteSocialAccountText', {magicWord: 'DELETE'}) }}</strong>
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<input
|
||||
v-model="password"
|
||||
class="form-control"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div
|
||||
id="feedback"
|
||||
class="col-12 form-group"
|
||||
>
|
||||
<label for="feedbackTextArea">{{ $t('feedback') }}</label>
|
||||
<textarea
|
||||
id="feedbackTextArea"
|
||||
v-model="feedback"
|
||||
class="form-control"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('neverMind') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
:disabled="!password"
|
||||
@click="deleteAccount()"
|
||||
>
|
||||
{{ $t('deleteDo') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
password: '',
|
||||
feedback: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'delete');
|
||||
},
|
||||
async deleteAccount () {
|
||||
await axios.delete('/api/v4/user', {
|
||||
data: {
|
||||
password: this.password,
|
||||
feedback: this.feedback,
|
||||
},
|
||||
});
|
||||
localStorage.clear();
|
||||
window.location.href = '/static/home';
|
||||
this.$root.$emit('bv::hide::modal', 'delete');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,158 +0,0 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<secondary-menu class="col-12">
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'site'}"
|
||||
exact="exact"
|
||||
:class="{'active': $route.name === 'site'}"
|
||||
>
|
||||
{{ $t('site') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'api'}"
|
||||
:class="{'active': $route.name === 'api'}"
|
||||
>
|
||||
{{ $t('API') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'dataExport'}"
|
||||
:class="{'active': $route.name === 'dataExport'}"
|
||||
>
|
||||
{{ $t('dataExport') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'promoCode'}"
|
||||
:class="{'active': $route.name === 'promoCode'}"
|
||||
>
|
||||
{{ $t('promoCode') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'subscription'}"
|
||||
:class="{'active': $route.name === 'subscription'}"
|
||||
>
|
||||
{{ $t('subscription') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
v-if="hasPermission(user, 'userSupport')"
|
||||
class="nav-link"
|
||||
:to="{name: 'transactions'}"
|
||||
:class="{'active': $route.name === 'transactions'}"
|
||||
>
|
||||
{{ $t('transactions') }}
|
||||
</router-link>
|
||||
<router-link
|
||||
class="nav-link"
|
||||
:to="{name: 'notifications'}"
|
||||
:class="{'active': $route.name === 'notifications'}"
|
||||
>
|
||||
{{ $t('notifications') }}
|
||||
</router-link>
|
||||
</secondary-menu>
|
||||
<div
|
||||
v-if="$route.name === 'subscription' && promo === 'g1g1'"
|
||||
class="g1g1-banner d-flex justify-content-center"
|
||||
@click="showSelectUser"
|
||||
>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon svg-gifts left-gift"
|
||||
v-html="icons.gifts"
|
||||
>
|
||||
</div>
|
||||
<div class="d-flex flex-column align-items-center text-center">
|
||||
<strong
|
||||
class="mt-auto mb-1"
|
||||
> {{ $t('g1g1Event') }} </strong>
|
||||
<p
|
||||
class="mb-auto"
|
||||
>
|
||||
{{ $t('g1g1Details') }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
v-once
|
||||
class="svg-icon svg-gifts right-gift"
|
||||
v-html="icons.gifts"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
strong {
|
||||
font-size: 1rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.g1g1-banner {
|
||||
color: $white;
|
||||
width: 100%;
|
||||
height: 5.75rem;
|
||||
background-image: linear-gradient(90deg, $teal-50 0%, $purple-400 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.left-gift {
|
||||
margin: auto 3rem auto auto;
|
||||
}
|
||||
|
||||
.right-gift {
|
||||
margin: auto auto auto 3rem;
|
||||
filter: flipH;
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.svg-gifts {
|
||||
width: 3.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import find from 'lodash/find';
|
||||
import { mapState } from '@/libs/store';
|
||||
import SecondaryMenu from '@/components/secondaryMenu';
|
||||
import gifts from '@/assets/svg/gifts-vertical.svg';
|
||||
import { userStateMixin } from '../../mixins/userState';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SecondaryMenu,
|
||||
},
|
||||
mixins: [userStateMixin],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gifts,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
currentEventList: 'worldState.data.currentEventList',
|
||||
}),
|
||||
currentEvent () {
|
||||
return find(this.currentEventList, event => Boolean(event.promo));
|
||||
},
|
||||
promo () {
|
||||
if (!this.currentEvent || !this.currentEvent.promo) return 'none';
|
||||
return this.currentEvent.promo;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showSelectUser () {
|
||||
this.$root.$emit('bv::show::modal', 'select-user-modal');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,143 +0,0 @@
|
||||
<template>
|
||||
<div class="row standard-page">
|
||||
<div class="col-12">
|
||||
<h1>{{ $t('notifications') }}</h1>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input
|
||||
v-model="user.preferences.pushNotifications.unsubscribeFromAll"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('pushNotifications', 'unsubscribeFromAll')"
|
||||
>
|
||||
<span>{{ $t('unsubscribeAllPush') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<br>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input
|
||||
v-model="user.preferences.emailNotifications.unsubscribeFromAll"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('emailNotifications', 'unsubscribeFromAll')"
|
||||
>
|
||||
<span>{{ $t('unsubscribeAllEmails') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<small>{{ $t('unsubscribeAllEmailsText') }}</small>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>
|
||||
<span>{{ $t('email') }}</span>
|
||||
</th>
|
||||
<th>
|
||||
<span>{{ $t('push') }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="notification in notificationsIds"
|
||||
:key="notification"
|
||||
>
|
||||
<td>
|
||||
<span>{{ $t(notification) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
v-model="user.preferences.emailNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('emailNotifications', notification)"
|
||||
>
|
||||
</td>
|
||||
<td v-if="onlyEmailsIds.indexOf(notification) === -1">
|
||||
<input
|
||||
v-model="user.preferences.pushNotifications[notification]"
|
||||
type="checkbox"
|
||||
@change="set('pushNotifications', notification)"
|
||||
>
|
||||
</td><td v-else>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import notificationsMixin from '@/mixins/notifications';
|
||||
|
||||
export default {
|
||||
mixins: [notificationsMixin],
|
||||
data () {
|
||||
return {
|
||||
notificationsIds: Object.freeze([
|
||||
'majorUpdates',
|
||||
'onboarding',
|
||||
'newPM',
|
||||
'wonChallenge',
|
||||
'giftedGems',
|
||||
'giftedSubscription',
|
||||
'invitedParty',
|
||||
'invitedGuild',
|
||||
'kickedGroup',
|
||||
'questStarted',
|
||||
'invitedQuest',
|
||||
'importantAnnouncements',
|
||||
'weeklyRecaps',
|
||||
'subscriptionReminders',
|
||||
]),
|
||||
// list of email-only notifications
|
||||
onlyEmailsIds: Object.freeze([
|
||||
'kickedGroup',
|
||||
'importantAnnouncements',
|
||||
'weeklyRecaps',
|
||||
'onboarding',
|
||||
'subscriptionReminders',
|
||||
]),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
async mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
subSection: this.$t('notifications'),
|
||||
});
|
||||
// If ?unsubFrom param is passed with valid email type,
|
||||
// automatically unsubscribe users from that email and
|
||||
// show an alert
|
||||
|
||||
// A simple object to map the key stored in the db (user.preferences.emailNotification[key])
|
||||
// to its string id but ONLY when the preferences' key and the string key don't match
|
||||
const MAP_PREF_TO_EMAIL_STRING = {
|
||||
importantAnnouncements: 'inactivityEmails',
|
||||
};
|
||||
|
||||
const { unsubFrom } = this.$route.query;
|
||||
|
||||
if (unsubFrom) {
|
||||
await this.$store.dispatch('user:set', {
|
||||
[`preferences.emailNotifications.${unsubFrom}`]: false,
|
||||
});
|
||||
|
||||
const emailTypeString = this.$t(MAP_PREF_TO_EMAIL_STRING[unsubFrom] || unsubFrom);
|
||||
this.text(this.$t('correctlyUnsubscribedEmailType', { emailType: emailTypeString }));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
set (preferenceType, notification) {
|
||||
const settings = {};
|
||||
settings[`preferences.${preferenceType}.${notification}`] = this.user.preferences[preferenceType][notification];
|
||||
this.$store.dispatch('user:set', settings);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,117 +0,0 @@
|
||||
<template>
|
||||
<div class="row standard-page">
|
||||
<div class="col-md-6">
|
||||
<h2>{{ $t('promoCode') }}</h2>
|
||||
<div
|
||||
class="form-inline"
|
||||
role="form"
|
||||
>
|
||||
<input
|
||||
v-model="couponCode"
|
||||
class="form-control"
|
||||
type="text"
|
||||
:placeholder="$t('promoPlaceholder')"
|
||||
>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="enterCoupon()"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<small>{{ $t('couponText') }}</small>
|
||||
</div>
|
||||
<div v-if="user.permissions.coupons">
|
||||
<hr>
|
||||
<h4>{{ $t('generateCodes') }}</h4>
|
||||
<div
|
||||
class="form"
|
||||
role="form"
|
||||
>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="codes.event"
|
||||
class="form-control"
|
||||
type="text"
|
||||
placeholder="Event code (eg, 'wondercon')"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="codes.count"
|
||||
class="form-control"
|
||||
type="number"
|
||||
placeholder="Number of codes to generate (eg, 250)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
@click="generateCodes(codes)"
|
||||
>
|
||||
{{ $t('generate') }}
|
||||
</button>
|
||||
<a
|
||||
class="btn btn-secondary"
|
||||
:href="getCodesUrl"
|
||||
>{{ $t('getCodes') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
import notifications from '@/mixins/notifications';
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
data () {
|
||||
return {
|
||||
codes: {
|
||||
event: '',
|
||||
count: '',
|
||||
},
|
||||
couponCode: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data', credentials: 'credentials' }),
|
||||
getCodesUrl () {
|
||||
if (!this.user) return '';
|
||||
return '/api/v4/coupons';
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
subSection: this.$t('promoCode'),
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
generateCodes () {
|
||||
// $http.post(ApiUrl.get() + '/api/v2/coupons/generate/
|
||||
// '+codes.event+'?count='+(codes.count || 1))
|
||||
// .success(function(res,code){
|
||||
// $scope._codes = {};
|
||||
// if (code!==200) return;
|
||||
// window.location.href = '/api/v2/coupons?limit='+codes.count+'&_id='+User.user._id+
|
||||
// '&apiToken='+User.settings.auth.apiToken;
|
||||
// })
|
||||
},
|
||||
async enterCoupon () {
|
||||
const code = await axios.post(`/api/v4/coupons/enter/${this.couponCode}`);
|
||||
if (!code) return;
|
||||
|
||||
this.$store.state.user.data = code.data.data;
|
||||
|
||||
this.text(this.$t('promoCodeApplied'));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div class="standard-page">
|
||||
<purchase-history-table
|
||||
:gem-transactions="gemTransactions"
|
||||
:hourglass-transactions="hourglassTransactions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import PurchaseHistoryTable from '../ui/purchaseHistoryTable.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PurchaseHistoryTable,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
gemTransactions: [],
|
||||
hourglassTransactions: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
async mounted () {
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
subSection: this.$t('transactions'),
|
||||
});
|
||||
|
||||
const history = await this.$store.dispatch('user:getPurchaseHistory');
|
||||
this.gemTransactions = history.filter(transaction => transaction.currency === 'gems');
|
||||
this.hourglassTransactions = history.filter(transaction => transaction.currency === 'hourglasses');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="reset"
|
||||
:title="$t('resetAccount')"
|
||||
:hide-footer="true"
|
||||
size="md"
|
||||
>
|
||||
<p>{{ $t('resetText1') }}</p>
|
||||
<p>{{ $t('resetText2') }}</p>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('neverMind') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="reset()"
|
||||
>
|
||||
{{ $t('resetDo') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'reset');
|
||||
},
|
||||
async reset () {
|
||||
await axios.post('/api/v4/user/reset');
|
||||
this.$router.push('/');
|
||||
setTimeout(() => window.location.reload(true), 100);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,210 +0,0 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="restore"
|
||||
:title="$t('fixValues')"
|
||||
:hide-footer="true"
|
||||
size="lg"
|
||||
>
|
||||
<p>{{ $t('fixValuesText1') }}</p>
|
||||
<p>{{ $t('fixValuesText2') }}</p>
|
||||
<div class="form-horizontal">
|
||||
<h3>{{ $t('stats') }}</h3>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('health') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.stats.hp"
|
||||
class="form-control"
|
||||
type="number"
|
||||
step="any"
|
||||
data-for="stats.hp"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('experience') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.stats.exp"
|
||||
class="form-control"
|
||||
type="number"
|
||||
step="any"
|
||||
data-for="stats.exp"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('gold') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.stats.gp"
|
||||
class="form-control"
|
||||
type="number"
|
||||
step="any"
|
||||
data-for="stats.gp"
|
||||
>
|
||||
</div>
|
||||
<!--input.form-control(type='number',
|
||||
step="any", data-for='stats.gp', v-model='restoreValues.stats.gp',disabled)-->
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('mana') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.stats.mp"
|
||||
class="form-control"
|
||||
type="number"
|
||||
step="any"
|
||||
data-for="stats.mp"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('level') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.stats.lvl"
|
||||
class="form-control"
|
||||
type="number"
|
||||
data-for="stats.lvl"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h3>{{ $t('achievements') }}</h3>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-3">
|
||||
<label class="control-label">{{ $t('fix21Streaks') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input
|
||||
v-model="restoreValues.achievements.streak"
|
||||
class="form-control"
|
||||
type="number"
|
||||
data-for="achievements.streak"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
class="btn btn-danger"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('discardChanges') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="restore()"
|
||||
>
|
||||
{{ $t('saveAndClose') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import clone from 'lodash/clone';
|
||||
import { MAX_LEVEL_HARD_CAP } from '@/../../common/script/constants';
|
||||
import { mapState } from '@/libs/store';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
restoreValues: {
|
||||
stats: {
|
||||
hp: 0,
|
||||
mp: 0,
|
||||
gp: 0,
|
||||
exp: 0,
|
||||
lvl: 0,
|
||||
},
|
||||
achievements: {
|
||||
streak: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
},
|
||||
mounted () {
|
||||
this.restoreValues.stats = clone(this.user.stats);
|
||||
this.restoreValues.achievements.streak = clone(this.user.achievements.streak);
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.validateInputs();
|
||||
this.$root.$emit('bv::hide::modal', 'restore');
|
||||
},
|
||||
restore () {
|
||||
if (!this.validateInputs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.restoreValues.stats.lvl > MAX_LEVEL_HARD_CAP) {
|
||||
this.restoreValues.stats.lvl = MAX_LEVEL_HARD_CAP;
|
||||
}
|
||||
|
||||
const userChangedLevel = this.restoreValues.stats.lvl !== this.user.stats.lvl;
|
||||
const userDidNotChangeExp = this.restoreValues.stats.exp === this.user.stats.exp;
|
||||
if (userChangedLevel && userDidNotChangeExp) this.restoreValues.stats.exp = 0;
|
||||
|
||||
this.user.stats = clone(this.restoreValues.stats);
|
||||
this.user.achievements.streak = clone(this.restoreValues.achievements.streak);
|
||||
|
||||
const settings = {
|
||||
'stats.hp': Number(this.restoreValues.stats.hp),
|
||||
'stats.exp': Number(this.restoreValues.stats.exp),
|
||||
'stats.gp': Number(this.restoreValues.stats.gp),
|
||||
'stats.lvl': Number(this.restoreValues.stats.lvl),
|
||||
'stats.mp': Number(this.restoreValues.stats.mp),
|
||||
'achievements.streak': Number(this.restoreValues.achievements.streak),
|
||||
};
|
||||
|
||||
this.$store.dispatch('user:set', settings);
|
||||
this.$root.$emit('bv::hide::modal', 'restore');
|
||||
},
|
||||
validateInputs () {
|
||||
const canRestore = ['hp', 'exp', 'gp', 'mp'];
|
||||
let valid = true;
|
||||
|
||||
for (const stat of canRestore) {
|
||||
if (this.restoreValues.stats[stat] === ''
|
||||
|| this.restoreValues.stats[stat] < 0
|
||||
) {
|
||||
this.restoreValues.stats[stat] = this.user.stats[stat];
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
const inputLevel = Number(this.restoreValues.stats.lvl);
|
||||
if (this.restoreValues.stats.lvl === ''
|
||||
|| !Number.isInteger(inputLevel)
|
||||
|| inputLevel < 1) {
|
||||
this.restoreValues.stats.lvl = this.user.stats.lvl;
|
||||
valid = false;
|
||||
}
|
||||
|
||||
const inputStreak = Number(this.restoreValues.achievements.streak);
|
||||
if (this.restoreValues.achievements.streak === ''
|
||||
|| !Number.isInteger(inputStreak)
|
||||
|| inputStreak < 0) {
|
||||
this.restoreValues.achievements.streak = this.user.achievements.streak;
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,859 +0,0 @@
|
||||
<template>
|
||||
<div class="row standard-page">
|
||||
<restore-modal />
|
||||
<reset-modal />
|
||||
<delete-modal />
|
||||
<h1 class="col-12">
|
||||
{{ $t('settings') }}
|
||||
</h1>
|
||||
<div class="col-sm-6">
|
||||
<div class="sleep">
|
||||
<h5>{{ $t('pauseDailies') }}</h5>
|
||||
<h4>{{ $t('sleepDescription') }}</h4>
|
||||
<ul>
|
||||
<li v-once>
|
||||
{{ $t('sleepBullet1') }}
|
||||
</li>
|
||||
<li v-once>
|
||||
{{ $t('sleepBullet2') }}
|
||||
</li>
|
||||
<li v-once>
|
||||
{{ $t('sleepBullet3') }}
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
v-if="!user.preferences.sleep"
|
||||
v-once
|
||||
class="sleep btn btn-primary btn-block pause-button"
|
||||
@click="toggleSleep()"
|
||||
>
|
||||
{{ $t('pauseDailies') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="user.preferences.sleep"
|
||||
v-once
|
||||
class="btn btn-secondary btn-block pause-button"
|
||||
@click="toggleSleep()"
|
||||
>
|
||||
{{ $t('unpauseDailies') }}
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-horizontal">
|
||||
<h5>{{ $t('language') }}</h5>
|
||||
<select
|
||||
class="form-control"
|
||||
:value="user.preferences.language"
|
||||
@change="changeLanguage($event)"
|
||||
>
|
||||
<option
|
||||
v-for="lang in availableLanguages"
|
||||
:key="lang.code"
|
||||
:value="lang.code"
|
||||
>
|
||||
{{ lang.name }}
|
||||
</option>
|
||||
</select>
|
||||
<small>
|
||||
{{ $t('americanEnglishGovern') }}
|
||||
<br>
|
||||
<strong v-html="$t('helpWithTranslation')"></strong>
|
||||
</small>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-horizontal">
|
||||
<h5>{{ $t('dateFormat') }}</h5>
|
||||
<select
|
||||
v-model="user.preferences.dateFormat"
|
||||
class="form-control"
|
||||
@change="set('dateFormat')"
|
||||
>
|
||||
<option
|
||||
v-for="dateFormat in availableFormats"
|
||||
:key="dateFormat"
|
||||
:value="dateFormat"
|
||||
>
|
||||
{{ dateFormat }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<h5>{{ $t('audioTheme') }}</h5>
|
||||
<select
|
||||
v-model="user.preferences.sound"
|
||||
class="form-control"
|
||||
@change="changeAudioTheme"
|
||||
>
|
||||
<option
|
||||
v-for="sound in availableAudioThemes"
|
||||
:key="sound"
|
||||
:value="sound"
|
||||
>
|
||||
{{ $t(`audioTheme_${sound}`) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-primary btn-xs"
|
||||
@click="playAudio"
|
||||
>
|
||||
{{ $t('demo') }}
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div
|
||||
v-if="hasClass"
|
||||
class="form-horizontal"
|
||||
>
|
||||
<h5>{{ $t('characterBuild') }}</h5>
|
||||
<h6 v-once>
|
||||
{{ $t('class') + ': ' }}
|
||||
<!-- @TODO: what is classText-->
|
||||
<!-- span(v-if='classText') {{ classText }} -->
|
||||
<button
|
||||
v-once
|
||||
class="btn btn-danger btn-xs"
|
||||
@click="changeClassForUser(true)"
|
||||
>
|
||||
{{ $t('changeClass') }}
|
||||
</button>
|
||||
<small class="cost">
|
||||
3 {{ $t('gems') }}
|
||||
<!-- @TODO add icon span.Pet_Currency_Gem1x.inline-gems-->
|
||||
</small>
|
||||
</h6>
|
||||
<hr>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="checkbox"
|
||||
id="preferenceAdvancedCollapsed"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
v-model="user.preferences.advancedCollapsed"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('advancedCollapsed')"
|
||||
>
|
||||
<span class="hint">
|
||||
{{ $t('startAdvCollapsed') }}
|
||||
</span>
|
||||
<b-popover
|
||||
target="preferenceAdvancedCollapsed"
|
||||
triggers="hover focus"
|
||||
placement="right"
|
||||
:prevent-overflow="false"
|
||||
:content="$t('startAdvCollapsedPop')"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
v-if="party.memberCount === 1"
|
||||
class="checkbox"
|
||||
id="preferenceDisplayInviteAtOneMember"
|
||||
>
|
||||
<label>
|
||||
<input
|
||||
v-model="user.preferences.displayInviteToPartyWhenPartyIs1"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('displayInviteToPartyWhenPartyIs1')"
|
||||
>
|
||||
<span class="hint">
|
||||
{{ $t('displayInviteToPartyWhenPartyIs1') }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input
|
||||
v-model="user.preferences.suppressModals.levelUp"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('suppressModals', 'levelUp')"
|
||||
>
|
||||
<label>{{ $t('suppressLevelUpModal') }}</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input
|
||||
v-model="user.preferences.suppressModals.hatchPet"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('suppressModals', 'hatchPet')"
|
||||
>
|
||||
<label>{{ $t('suppressHatchPetModal') }}</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input
|
||||
v-model="user.preferences.suppressModals.raisePet"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('suppressModals', 'raisePet')"
|
||||
>
|
||||
<label>{{ $t('suppressRaisePetModal') }}</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<input
|
||||
v-model="user.preferences.suppressModals.streak"
|
||||
type="checkbox"
|
||||
class="mr-2"
|
||||
@change="set('suppressModals', 'streak')"
|
||||
>
|
||||
<label>{{ $t('suppressStreakModal') }}</label>
|
||||
</div>
|
||||
<hr>
|
||||
<button
|
||||
id="buttonShowBailey"
|
||||
class="btn btn-primary mr-2 mb-2"
|
||||
@click="showBailey()"
|
||||
>
|
||||
{{ $t('showBailey') }}
|
||||
<b-popover
|
||||
target="buttonShowBailey"
|
||||
triggers="hover focus"
|
||||
placement="right"
|
||||
:prevent-overflow="false"
|
||||
:content="$t('showBaileyPop')"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
id="buttonFCV"
|
||||
class="btn btn-primary mr-2 mb-2"
|
||||
@click="openRestoreModal()"
|
||||
>
|
||||
{{ $t('fixVal') }}
|
||||
<b-popover
|
||||
target="buttonFCV"
|
||||
triggers="hover focus"
|
||||
placement="right"
|
||||
:prevent-overflow="false"
|
||||
:content="$t('fixValPop')"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="user.preferences.disableClasses == true"
|
||||
id="buttonEnableClasses"
|
||||
class="btn btn-primary mb-2"
|
||||
@click="changeClassForUser(false)"
|
||||
>
|
||||
{{ $t('enableClass') }}
|
||||
<b-popover
|
||||
target="buttonEnableClasses"
|
||||
triggers="hover focus"
|
||||
placement="right"
|
||||
:prevent-overflow="false"
|
||||
:content="$t('enableClassPop')"
|
||||
/>
|
||||
</button>
|
||||
<hr>
|
||||
<day-start-adjustment />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h2>{{ $t('registration') }}</h2>
|
||||
<div class="panel-body">
|
||||
<div>
|
||||
<ul class="list-inline">
|
||||
<li
|
||||
v-for="network in SOCIAL_AUTH_NETWORKS"
|
||||
:key="network.key"
|
||||
>
|
||||
<button
|
||||
v-if="!user.auth[network.key].id && network.key !== 'facebook'"
|
||||
class="btn btn-primary mb-2"
|
||||
@click="socialAuth(network.key, user)"
|
||||
>
|
||||
{{ $t('registerWithSocial', {network: network.name}) }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!hasBackupAuthOption(network.key) && user.auth[network.key].id"
|
||||
class="btn btn-primary mb-2"
|
||||
disabled="disabled"
|
||||
>
|
||||
{{ $t('registeredWithSocial', {network: network.name}) }}
|
||||
</button>
|
||||
<button
|
||||
v-if="hasBackupAuthOption(network.key) && user.auth[network.key].id"
|
||||
class="btn btn-danger"
|
||||
@click="deleteSocialAuth(network)"
|
||||
>
|
||||
{{ $t('detachSocial', {network: network.name}) }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<div v-if="!user.auth.local.has_password">
|
||||
<h5 v-if="!user.auth.local.email">
|
||||
{{ $t('addLocalAuth') }}
|
||||
</h5>
|
||||
<h5 v-if="user.auth.local.email">
|
||||
{{ $t('addPasswordAuth') }}
|
||||
</h5>
|
||||
<div
|
||||
class="form"
|
||||
name="localAuth"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div
|
||||
v-if="!user.auth.local.email"
|
||||
class="form-group"
|
||||
>
|
||||
<input
|
||||
v-model="localAuth.email"
|
||||
class="form-control"
|
||||
type="text"
|
||||
:placeholder="$t('email')"
|
||||
required="required"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="localAuth.password"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('password')"
|
||||
required="required"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="localAuth.confirmPassword"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('confirmPass')"
|
||||
required="required"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
@click="addLocalAuth()"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="usersettings">
|
||||
<h5>{{ $t('changeDisplayName') }}</h5>
|
||||
<div
|
||||
class="form"
|
||||
name="changeDisplayName"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div class="form-group">
|
||||
<input
|
||||
id="changeDisplayname"
|
||||
v-model="temporaryDisplayName"
|
||||
class="form-control"
|
||||
type="text"
|
||||
:placeholder="$t('newDisplayName')"
|
||||
:class="{'is-invalid input-invalid': displayNameInvalid}"
|
||||
>
|
||||
<div
|
||||
v-if="displayNameIssues.length > 0"
|
||||
class="mb-3"
|
||||
>
|
||||
<div
|
||||
v-for="issue in displayNameIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
:disabled="displayNameCannotSubmit"
|
||||
@click="changeDisplayName(temporaryDisplayName)"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<h5>{{ $t('changeUsername') }}</h5>
|
||||
<div
|
||||
class="form"
|
||||
name="changeUsername"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div
|
||||
v-if="verifiedUsername"
|
||||
class="iconalert iconalert-success"
|
||||
>
|
||||
{{ $t('usernameVerifiedConfirmation', {'username': user.auth.local.username}) }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="iconalert iconalert-warning"
|
||||
>
|
||||
<div class="align-middle">
|
||||
<span>{{ $t('usernameNotVerified') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
id="changeUsername"
|
||||
v-model="usernameUpdates.username"
|
||||
class="form-control"
|
||||
type="text"
|
||||
:placeholder="$t('newUsername')"
|
||||
:class="{'is-invalid input-invalid': usernameInvalid}"
|
||||
@blur="restoreEmptyUsername()"
|
||||
>
|
||||
<div
|
||||
v-for="issue in usernameIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
<small class="form-text text-muted">{{ $t('changeUsernameDisclaimer') }}</small>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
:disabled="usernameCannotSubmit"
|
||||
@click="changeUser('username', usernameUpdates)"
|
||||
>
|
||||
{{ $t('saveAndConfirm') }}
|
||||
</button>
|
||||
</div>
|
||||
<h5 v-if="user.auth.local.has_password">
|
||||
{{ $t('changeEmail') }}
|
||||
</h5>
|
||||
<div
|
||||
v-if="user.auth.local.email"
|
||||
class="form"
|
||||
name="changeEmail"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div class="form-group">
|
||||
<input
|
||||
id="changeEmail"
|
||||
v-model="emailUpdates.newEmail"
|
||||
class="form-control"
|
||||
type="text"
|
||||
:placeholder="$t('newEmail')"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="user.auth.local.has_password"
|
||||
class="form-group"
|
||||
>
|
||||
<input
|
||||
v-model="emailUpdates.password"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('password')"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
@click="changeUser('email', emailUpdates)"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<h5 v-if="user.auth.local.has_password">
|
||||
{{ $t('changePass') }}
|
||||
</h5>
|
||||
<div
|
||||
v-if="user.auth.local.has_password"
|
||||
class="form"
|
||||
name="changePassword"
|
||||
novalidate="novalidate"
|
||||
>
|
||||
<div class="form-group">
|
||||
<input
|
||||
id="changePassword"
|
||||
v-model="passwordUpdates.password"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('oldPass')"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="passwordUpdates.newPassword"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('newPass')"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
v-model="passwordUpdates.confirmPassword"
|
||||
class="form-control"
|
||||
type="password"
|
||||
:placeholder="$t('confirmPass')"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="submit"
|
||||
@click="changeUser('password', passwordUpdates)"
|
||||
>
|
||||
{{ $t('submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
<div>
|
||||
<h5>{{ $t('dangerZone') }}</h5>
|
||||
<div>
|
||||
<button
|
||||
v-b-popover.hover.auto="$t('resetAccPop')"
|
||||
class="btn btn-danger mr-2 mb-2"
|
||||
popover-trigger="mouseenter"
|
||||
popover-placement="right"
|
||||
@click="openResetModal()"
|
||||
>
|
||||
{{ $t('resetAccount') }}
|
||||
</button>
|
||||
<button
|
||||
v-b-popover.hover.auto="$t('deleteAccPop')"
|
||||
class="btn btn-danger mb-2"
|
||||
popover-trigger="mouseenter"
|
||||
@click="openDeleteModal()"
|
||||
>
|
||||
{{ $t('deleteAccount') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
input {
|
||||
color: $gray-50;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: fit-content;
|
||||
}
|
||||
.usersettings h5 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.iconalert > div > span {
|
||||
line-height: 25px;
|
||||
}
|
||||
.iconalert > div:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.input-error {
|
||||
color: $red-50;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.sleep {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import hello from 'hellojs';
|
||||
import axios from 'axios';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { mapState } from '@/libs/store';
|
||||
import restoreModal from './restoreModal';
|
||||
import resetModal from './resetModal';
|
||||
import deleteModal from './deleteModal';
|
||||
import dayStartAdjustment from './dayStartAdjustment';
|
||||
import { SUPPORTED_SOCIAL_NETWORKS } from '@/../../common/script/constants';
|
||||
import changeClass from '@/../../common/script/ops/changeClass';
|
||||
import notificationsMixin from '../../mixins/notifications';
|
||||
import sounds from '../../libs/sounds';
|
||||
import { buildAppleAuthUrl } from '../../libs/auth';
|
||||
|
||||
// @TODO: this needs our window.env fix
|
||||
// import { availableLanguages } from '../../../server/libs/i18n';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
restoreModal,
|
||||
resetModal,
|
||||
deleteModal,
|
||||
dayStartAdjustment,
|
||||
},
|
||||
mixins: [notificationsMixin],
|
||||
data () {
|
||||
return {
|
||||
SOCIAL_AUTH_NETWORKS: [],
|
||||
party: {},
|
||||
// Made available by the server as a script
|
||||
availableFormats: ['MM/dd/yyyy', 'dd/MM/yyyy', 'yyyy/MM/dd'],
|
||||
temporaryDisplayName: '',
|
||||
usernameUpdates: { username: '' },
|
||||
emailUpdates: {},
|
||||
passwordUpdates: {},
|
||||
localAuth: {
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
displayNameIssues: [],
|
||||
usernameIssues: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
availableLanguages: 'i18n.availableLanguages',
|
||||
content: 'content',
|
||||
}),
|
||||
availableAudioThemes () {
|
||||
return ['off', ...this.content.audioThemes];
|
||||
},
|
||||
hasClass () {
|
||||
return this.$store.getters['members:hasClass'](this.user);
|
||||
},
|
||||
verifiedUsername () {
|
||||
return this.user.flags.verifiedUsername;
|
||||
},
|
||||
displayNameInvalid () {
|
||||
if (this.temporaryDisplayName.length <= 1) return false;
|
||||
return !this.displayNameValid;
|
||||
},
|
||||
displayNameValid () {
|
||||
if (this.temporaryDisplayName.length <= 1) return false;
|
||||
return this.displayNameIssues.length === 0;
|
||||
},
|
||||
displayNameCannotSubmit () {
|
||||
if (this.temporaryDisplayName.length <= 1) return true;
|
||||
return !this.displayNameValid;
|
||||
},
|
||||
usernameValid () {
|
||||
if (this.usernameUpdates.username.length <= 1) return false;
|
||||
return this.usernameIssues.length === 0;
|
||||
},
|
||||
usernameInvalid () {
|
||||
if (this.usernameUpdates.username.length <= 1) return false;
|
||||
return !this.usernameValid;
|
||||
},
|
||||
usernameCannotSubmit () {
|
||||
if (this.usernameUpdates.username.length <= 1) return true;
|
||||
return !this.usernameValid;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
usernameUpdates: {
|
||||
handler () {
|
||||
this.validateUsername(this.usernameUpdates.username);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
temporaryDisplayName: {
|
||||
handler () {
|
||||
this.validateDisplayName(this.temporaryDisplayName);
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.SOCIAL_AUTH_NETWORKS = SUPPORTED_SOCIAL_NETWORKS;
|
||||
// @TODO: We may need to request the party here
|
||||
this.party = this.$store.state.party;
|
||||
this.usernameUpdates.username = this.user.auth.local.username || null;
|
||||
this.temporaryDisplayName = this.user.profile.name;
|
||||
this.emailUpdates.newEmail = this.user.auth.local.email || null;
|
||||
this.localAuth.username = this.user.auth.local.username || null;
|
||||
this.soundIndex = 0;
|
||||
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
section: this.$t('settings'),
|
||||
});
|
||||
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line no-process-env
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line no-process-env
|
||||
}, {
|
||||
redirect_uri: '', // eslint-disable-line
|
||||
});
|
||||
|
||||
const focusID = this.$route.query.focus;
|
||||
if (focusID !== undefined && focusID !== null) {
|
||||
this.$nextTick(() => {
|
||||
const element = document.getElementById(focusID);
|
||||
if (element !== undefined && element !== null) {
|
||||
element.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleSleep () {
|
||||
this.$store.dispatch('user:sleep');
|
||||
},
|
||||
validateDisplayName: debounce(function checkName (displayName) {
|
||||
if (displayName.length <= 1 || displayName === this.user.profile.name) {
|
||||
this.displayNameIssues = [];
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('auth:verifyDisplayName', {
|
||||
displayName,
|
||||
}).then(res => {
|
||||
if (res.issues !== undefined) {
|
||||
this.displayNameIssues = res.issues;
|
||||
} else {
|
||||
this.displayNameIssues = [];
|
||||
}
|
||||
});
|
||||
}, 500),
|
||||
validateUsername: debounce(function checkName (username) {
|
||||
if (username.length <= 1 || username === this.user.auth.local.username) {
|
||||
this.usernameIssues = [];
|
||||
return;
|
||||
}
|
||||
this.$store.dispatch('auth:verifyUsername', {
|
||||
username,
|
||||
}).then(res => {
|
||||
if (res.issues !== undefined) {
|
||||
this.usernameIssues = res.issues;
|
||||
} else {
|
||||
this.usernameIssues = [];
|
||||
}
|
||||
});
|
||||
}, 500),
|
||||
set (preferenceType, subtype) {
|
||||
const settings = {};
|
||||
if (!subtype) {
|
||||
settings[`preferences.${preferenceType}`] = this.user.preferences[preferenceType];
|
||||
} else {
|
||||
settings[`preferences.${preferenceType}.${subtype}`] = this.user.preferences[preferenceType][subtype];
|
||||
}
|
||||
return this.$store.dispatch('user:set', settings);
|
||||
},
|
||||
hideHeader () {
|
||||
this.set('hideHeader');
|
||||
if (!this.user.preferences.hideHeader || !this.user.preferences.stickyHeader) return;
|
||||
this.user.preferences.hideHeader = false;
|
||||
this.set('stickyHeader');
|
||||
},
|
||||
toggleStickyHeader () {
|
||||
this.set('stickyHeader');
|
||||
},
|
||||
showTour () {
|
||||
// @TODO: Do we still use this?
|
||||
// User.set({'flags.showTour':true});
|
||||
// Guide.goto('intro', 0, true);
|
||||
},
|
||||
showBailey () {
|
||||
this.$root.$emit('bv::show::modal', 'new-stuff');
|
||||
},
|
||||
hasBackupAuthOption (networkKeyToCheck) {
|
||||
if (this.user.auth.local.username) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.SOCIAL_AUTH_NETWORKS.find(network => {
|
||||
if (network.key !== networkKeyToCheck) {
|
||||
if (this.user.auth[network.key]) {
|
||||
return !!this.user.auth[network.key].id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
async changeLanguage (e) {
|
||||
const newLang = e.target.value;
|
||||
this.user.preferences.language = newLang;
|
||||
await this.set('language');
|
||||
setTimeout(() => window.location.reload(true));
|
||||
},
|
||||
async changeUser (attribute, updates) {
|
||||
await axios.put(`/api/v4/user/auth/update-${attribute}`, updates);
|
||||
if (attribute === 'username') {
|
||||
this.user.auth.local.username = updates[attribute];
|
||||
this.localAuth.username = this.user.auth.local.username;
|
||||
this.user.flags.verifiedUsername = true;
|
||||
} else if (attribute === 'email') {
|
||||
this.user.auth.local.email = updates.newEmail;
|
||||
window.alert(this.$t('emailSuccess')); // eslint-disable-line no-alert
|
||||
} else if (attribute === 'password') {
|
||||
this.passwordUpdates = {};
|
||||
this.$store.dispatch('snackbars:add', {
|
||||
title: 'Habitica',
|
||||
text: this.$t('passwordSuccess'),
|
||||
type: 'success',
|
||||
timeout: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
async changeDisplayName (newName) {
|
||||
await axios.put('/api/v4/user/', { 'profile.name': newName });
|
||||
window.alert(this.$t('displayNameSuccess')); // eslint-disable-line no-alert
|
||||
this.user.profile.name = newName;
|
||||
this.temporaryDisplayName = newName;
|
||||
},
|
||||
openRestoreModal () {
|
||||
this.$root.$emit('bv::show::modal', 'restore');
|
||||
},
|
||||
openResetModal () {
|
||||
this.$root.$emit('bv::show::modal', 'reset');
|
||||
},
|
||||
openDeleteModal () {
|
||||
this.$root.$emit('bv::show::modal', 'delete');
|
||||
},
|
||||
async deleteSocialAuth (network) {
|
||||
await axios.delete(`/api/v4/user/auth/social/${network.key}`);
|
||||
this.user.auth[network.key] = {};
|
||||
this.text(this.$t('detachedSocial', { network: network.name }));
|
||||
},
|
||||
async socialAuth (network) {
|
||||
if (network === 'apple') {
|
||||
window.location.href = buildAppleAuthUrl();
|
||||
} else {
|
||||
const auth = await hello(network).login({ scope: 'email' });
|
||||
await this.$store.dispatch('auth:socialAuth', {
|
||||
auth,
|
||||
});
|
||||
window.location.href = '/';
|
||||
}
|
||||
},
|
||||
async changeClassForUser (confirmationNeeded) {
|
||||
if (confirmationNeeded && !window.confirm(this.$t('changeClassConfirmCost'))) return; // eslint-disable-line no-alert
|
||||
try {
|
||||
changeClass(this.user);
|
||||
await axios.post('/api/v4/user/change-class');
|
||||
} catch (e) {
|
||||
window.alert(e.message); // eslint-disable-line no-alert
|
||||
}
|
||||
},
|
||||
async addLocalAuth () {
|
||||
if (this.localAuth.email === '') {
|
||||
this.localAuth.email = this.user.auth.local.email;
|
||||
}
|
||||
await axios.post('/api/v4/user/auth/local/register', this.localAuth);
|
||||
window.location.href = '/user/settings/site';
|
||||
},
|
||||
restoreEmptyUsername () {
|
||||
if (this.usernameUpdates.username.length < 1) {
|
||||
this.usernameUpdates.username = this.user.auth.local.username;
|
||||
}
|
||||
},
|
||||
changeAudioTheme () {
|
||||
this.soundIndex = 0;
|
||||
this.set('sound');
|
||||
},
|
||||
playAudio () {
|
||||
this.$root.$emit('playSound', sounds[this.soundIndex]);
|
||||
this.soundIndex = (this.soundIndex + 1) % sounds.length;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -115,8 +115,6 @@
|
||||
}
|
||||
|
||||
.input-error {
|
||||
color: $red-50;
|
||||
font-size: 90%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
:key="item.key"
|
||||
:item="item"
|
||||
:price="item.value"
|
||||
:item-content-class="'shop_'+item.key"
|
||||
:item-content-class="`shop_${item.key}`"
|
||||
:empty-item="false"
|
||||
:popover-position="'top'"
|
||||
@click="featuredItemSelected(item)"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div
|
||||
v-once
|
||||
class="gem-price-div"
|
||||
:class="{'background': withBackground}"
|
||||
>
|
||||
<div
|
||||
:class="`mr-2 svg-icon gem icon-${iconSize}`"
|
||||
v-html="icons.gem"
|
||||
></div>
|
||||
<span class="gem-price">{{ gemPrice }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gemIcon from '@/assets/svg/gem.svg';
|
||||
|
||||
export default {
|
||||
name: 'GemPrice',
|
||||
props: ['gemPrice', 'iconSize', 'withBackground'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
gem: gemIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.gem-price {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
|
||||
color: $green-10;
|
||||
}
|
||||
|
||||
.gem-price-div {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.background {
|
||||
align-self: center;
|
||||
border-radius: 20px;
|
||||
padding: 6px 20px;
|
||||
|
||||
background-color: rgba($green-100, 0.15);
|
||||
}
|
||||
</style>
|
||||
@@ -92,7 +92,7 @@
|
||||
:item="item"
|
||||
:price="item.goldValue ? item.goldValue : item.value"
|
||||
:price-type="item.goldValue ? 'gold' : 'gem'"
|
||||
:item-content-class="'inventory_quest_scroll_'+item.key"
|
||||
:item-content-class="`inventory_quest_scroll_${item.key}`"
|
||||
:empty-item="false"
|
||||
:popover-position="'top'"
|
||||
@click="selectItem(item)"
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
<item-with-label
|
||||
v-for="drop in getDropsList(quest.drop.items, false)"
|
||||
:key="drop.type+'_'+drop.key"
|
||||
:key="`${drop.type}_${drop.key}`"
|
||||
:item="{}"
|
||||
class="item-with-label"
|
||||
>
|
||||
|
||||
@@ -880,8 +880,6 @@ export default {
|
||||
},
|
||||
mounted () {
|
||||
hello.init({
|
||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
||||
// windows: WINDOWS_CLIENT_ID,
|
||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||
});
|
||||
this.$store.dispatch('common:setTitle', {
|
||||
|
||||
@@ -682,7 +682,9 @@ export default {
|
||||
// as default filter for daily
|
||||
// and set the filter as 'due' only when the component first
|
||||
// loads and not on subsequent reloads.
|
||||
if (type === 'daily' && filter === '' && !this.challenge) {
|
||||
if (
|
||||
type === 'daily' && filter === '' && !this.challenge
|
||||
) {
|
||||
filter = 'due'; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
</span>
|
||||
<label
|
||||
v-once
|
||||
class="mb-1"
|
||||
v-html="text"
|
||||
></label>
|
||||
</div>
|
||||
@@ -23,11 +22,11 @@
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
label {
|
||||
height: 1.5rem;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 1.71;
|
||||
letter-spacing: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gray-200 {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<div>
|
||||
<select-list
|
||||
:items="items"
|
||||
:key-prop="'icon'"
|
||||
class="difficulty-select"
|
||||
:class="{disabled: disabled}"
|
||||
:disabled="disabled"
|
||||
@@ -10,7 +9,7 @@
|
||||
:hide-icon="true"
|
||||
@select="$emit('select', $event.value)"
|
||||
>
|
||||
<template v-slot:item="{ item, button }">
|
||||
<template #item="{ item, button }">
|
||||
<div
|
||||
v-if="item"
|
||||
class="difficulty-item"
|
||||
|
||||
@@ -203,16 +203,15 @@
|
||||
<template
|
||||
v-if="task.type !== 'reward'"
|
||||
>
|
||||
<div class="d-flex mt-3">
|
||||
<div class="d-flex mt-3 align-items-center">
|
||||
<lockable-label
|
||||
:locked="challengeAccessRequired"
|
||||
:text="$t('difficulty')"
|
||||
/>
|
||||
<div
|
||||
v-b-tooltip.hover.righttop="$t('difficultyHelp')"
|
||||
class="svg-icon info-icon mb-auto ml-1"
|
||||
v-html="icons.information"
|
||||
></div>
|
||||
<information-icon
|
||||
tooltip-id="difficultyHelp"
|
||||
:tooltip="$t('difficultyHelp')"
|
||||
/>
|
||||
</div>
|
||||
<select-difficulty
|
||||
:value="task.priority"
|
||||
@@ -452,7 +451,7 @@
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
v-if="task.type === 'daily' && isUserTask && purpose === 'edit'"
|
||||
v-if="advancedSettingsShowRestoreStreak"
|
||||
class="option mt-3"
|
||||
>
|
||||
<div class="form-group">
|
||||
@@ -479,8 +478,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="task.type === 'habit'
|
||||
&& isUserTask && purpose === 'edit' && (task.up || task.down)"
|
||||
v-if="advancedSettingsShowAdjustCounter"
|
||||
class="option mt-3"
|
||||
>
|
||||
<div class="form-group">
|
||||
@@ -539,6 +537,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="advancedSettingsShowTaskAlias"
|
||||
class="option mt-3"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label
|
||||
v-once
|
||||
class="mb-1"
|
||||
>{{ $t('taskAlias') }}
|
||||
|
||||
<information-icon
|
||||
tooltip-id="taskAlias"
|
||||
:tooltip="$t('taskAliasPopover')"
|
||||
/>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="task.alias"
|
||||
class="form-control"
|
||||
:placeholder="$t('taskAliasPlaceholder')"
|
||||
type="text"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
@@ -882,6 +905,11 @@
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.habit-option {
|
||||
&-container {
|
||||
min-width: 3rem;
|
||||
@@ -997,7 +1025,6 @@ import lockableLabel from '@/components/tasks/modal-controls/lockableLabel';
|
||||
|
||||
import syncTask from '../../mixins/syncTask';
|
||||
|
||||
import informationIcon from '@/assets/svg/information.svg';
|
||||
import positiveIcon from '@/assets/svg/positive.svg';
|
||||
import negativeIcon from '@/assets/svg/negative.svg';
|
||||
import streakIcon from '@/assets/svg/streak.svg';
|
||||
@@ -1006,10 +1033,12 @@ import goldIcon from '@/assets/svg/gold.svg';
|
||||
import chevronIcon from '@/assets/svg/chevron.svg';
|
||||
import calendarIcon from '@/assets/svg/calendar.svg';
|
||||
import gripIcon from '@/assets/svg/grip.svg';
|
||||
import InformationIcon from '@/components/ui/informationIcon.vue';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InformationIcon,
|
||||
SelectMulti,
|
||||
Datepicker,
|
||||
checklist,
|
||||
@@ -1029,7 +1058,6 @@ export default {
|
||||
showAssignedSelect: false,
|
||||
newChecklistItem: null,
|
||||
icons: Object.freeze({
|
||||
information: informationIcon,
|
||||
negative: negativeIcon,
|
||||
positive: positiveIcon,
|
||||
destroy: deleteIcon,
|
||||
@@ -1064,25 +1092,25 @@ export default {
|
||||
dayMapping: 'constants.DAY_MAPPING',
|
||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||
}),
|
||||
advancedSettingsAvailable () {
|
||||
if (
|
||||
this.task.type === 'reward'
|
||||
|| this.task.type === 'todo'
|
||||
|| this.purpose === 'create'
|
||||
|| !this.isUserTask
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.task.type === 'habit'
|
||||
&& !this.task.up
|
||||
&& !this.task.down
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// region advanced settings
|
||||
advancedSettingsShowAdjustCounter () {
|
||||
return this.task.type === 'habit'
|
||||
&& this.isUserTask && this.purpose === 'edit'
|
||||
&& (this.task.up || this.task.down);
|
||||
},
|
||||
advancedSettingsShowRestoreStreak () {
|
||||
return this.task.type === 'daily' && this.isUserTask
|
||||
&& this.purpose === 'edit';
|
||||
},
|
||||
advancedSettingsShowTaskAlias () {
|
||||
return this.isUserTask && this.user.preferences.developerMode;
|
||||
},
|
||||
advancedSettingsAvailable () {
|
||||
return this.advancedSettingsShowRestoreStreak
|
||||
|| this.advancedSettingsShowAdjustCounter
|
||||
|| this.advancedSettingsShowTaskAlias;
|
||||
},
|
||||
// endregion advanced settings
|
||||
checklistEnabled () {
|
||||
return ['daily', 'todo'].indexOf(this.task.type) > -1 && !this.isOriginalChallengeTask;
|
||||
},
|
||||
@@ -1157,7 +1185,6 @@ export default {
|
||||
},
|
||||
},
|
||||
async mounted () {
|
||||
this.showAdvancedOptions = !this.user.preferences.advancedCollapsed;
|
||||
if (this.groupId) {
|
||||
const groupResponse = await axios.get(`/api/v4/groups/${this.groupId}`);
|
||||
this.managers = Object.keys(groupResponse.data.data.managers);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<span class="ml-1">
|
||||
<div
|
||||
:id="`tooltip_${tooltipId}`"
|
||||
class="svg-icon icon-16"
|
||||
:title="tooltip"
|
||||
v-html="icons.information"
|
||||
></div>
|
||||
<b-tooltip
|
||||
:title="tooltip"
|
||||
:target="`tooltip_${tooltipId}`"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import informationIcon from '@/assets/svg/information.svg';
|
||||
|
||||
export default {
|
||||
name: 'InformationIcon',
|
||||
props: ['tooltipId', 'tooltip'],
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
information: informationIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -9,7 +9,7 @@
|
||||
@show="isOpened = true"
|
||||
@hide="isOpened = false"
|
||||
>
|
||||
<template v-slot:button-content>
|
||||
<template #button-content>
|
||||
<slot
|
||||
name="item"
|
||||
:item="selected || placeholder"
|
||||
@@ -21,13 +21,13 @@
|
||||
</template>
|
||||
<b-dropdown-item
|
||||
v-for="item in items"
|
||||
:key="keyProp ? item[keyProp] : item"
|
||||
:disabled="typeof item[disabledProp] === 'undefined' ? false : item[disabledProp]"
|
||||
:active="item === selected"
|
||||
:key="getKeyProp(item)"
|
||||
:disabled="isDisabled(item)"
|
||||
:active="isSelected(item)"
|
||||
:class="{
|
||||
active: item === selected,
|
||||
active: isSelected(item),
|
||||
selectListItem: true,
|
||||
showIcon: !hideIcon && item === selected
|
||||
showIcon: !hideIcon && isSelected(item)
|
||||
}"
|
||||
@click="selectItem(item)"
|
||||
>
|
||||
@@ -51,39 +51,39 @@
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
.select-list ::v-deep {
|
||||
.dropdown-toggle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 25px; /* To allow enough room for the down arrow to be displayed */
|
||||
.select-list ::v-deep {
|
||||
.dropdown-toggle {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 25px; /* To allow enough room for the down arrow to be displayed */
|
||||
}
|
||||
|
||||
.selectListItem {
|
||||
position: relative;
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.selectListItem {
|
||||
position: relative;
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
&:not(.showIcon) {
|
||||
.svg-icon.check-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.showIcon) {
|
||||
.svg-icon.check-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-icon.check-icon.color {
|
||||
margin-left: 10px; /* So the flex item (checkmark) will have some spacing from the text */
|
||||
width: 0.77rem;
|
||||
height: 0.615rem;
|
||||
color: $purple-300;
|
||||
}
|
||||
.svg-icon.check-icon.color {
|
||||
margin-left: 10px; /* So the flex item (checkmark) will have some spacing from the text */
|
||||
width: 0.77rem;
|
||||
height: 0.615rem;
|
||||
color: $purple-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -101,6 +101,9 @@ export default {
|
||||
keyProp: {
|
||||
type: String,
|
||||
},
|
||||
activeKeyProp: {
|
||||
type: String,
|
||||
},
|
||||
disabledProp: {
|
||||
type: String,
|
||||
},
|
||||
@@ -128,10 +131,23 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getKeyProp (item) {
|
||||
return this.keyProp ? item[this.keyProp] : item;
|
||||
},
|
||||
isDisabled (item) {
|
||||
return typeof item[this.disabledProp] === 'undefined' ? false : item[this.disabledProp];
|
||||
},
|
||||
selectItem (item) {
|
||||
this.selected = item;
|
||||
this.selected = this.getKeyProp(item);
|
||||
this.$emit('select', item);
|
||||
},
|
||||
isSelected (item) {
|
||||
if (this.activeKeyProp) {
|
||||
return item[this.activeKeyProp] === this.selected;
|
||||
}
|
||||
|
||||
return item === this.selected;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="label-line">
|
||||
<div
|
||||
v-if="settingsLabel"
|
||||
class="settings-label"
|
||||
>
|
||||
{{ $t(settingsLabel) }}
|
||||
</div>
|
||||
|
||||
<slot name="top-right"></slot>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div
|
||||
class="input-group"
|
||||
:class="{
|
||||
'is-valid': validStyle,
|
||||
'is-invalid': invalidStyle
|
||||
}"
|
||||
>
|
||||
<input
|
||||
:value="value"
|
||||
class="form-control"
|
||||
:type="inputType"
|
||||
:class="{
|
||||
'is-invalid input-invalid': invalidStyle,
|
||||
'is-valid input-valid': validStyle
|
||||
}"
|
||||
:readonly="readonly"
|
||||
:aria-readonly="readonly"
|
||||
|
||||
:placeholder="placeholder"
|
||||
@keyup="handleChange"
|
||||
@blur="$emit('blur')"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-for="issue in invalidIssues"
|
||||
:key="issue"
|
||||
class="input-error"
|
||||
>
|
||||
{{ issue }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'ValidatedTextInput',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'update:value',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isValid: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
onlyShowInvalidState: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inputType: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
settingsLabel: {
|
||||
type: String,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
},
|
||||
invalidIssues: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
wasChanged: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
canChangeClasses () {
|
||||
return !this.readonly && this.wasChanged;
|
||||
},
|
||||
validStyle () {
|
||||
return this.canChangeClasses && this.isValid && !this.onlyShowInvalidState;
|
||||
},
|
||||
invalidStyle () {
|
||||
return this.canChangeClasses && !this.isValid;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange ({ target: { value } }) {
|
||||
this.wasChanged = true;
|
||||
this.$emit('update:value', value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.label-line {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.settings-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user