Compare commits

...

19 Commits

Author SHA1 Message Date
Kalista Payne
4ecd13fbdc chore(github): split responsiveness to #15514 2025-09-17 16:25:19 -05:00
Hafiz
8ac414a184 move !error.response to correct level
!error.response before any attempt to access error.response.status
2025-09-15 12:03:22 -05:00
Kalista Payne
c4c0cca369 fix(admin): revert accidental change from rebase 2025-09-12 18:21:52 -05:00
Kalista Payne
326843fdc5 fix(blockers): duplicated code from rebase 2025-09-12 18:05:30 -05:00
Hafiz
0df9ea32fb Revert "Merge branch 'fiz/item-container-scaling' into qa/bat"
This reverts commit 4f28bfaad4, reversing
changes made to 477dd6328a.
2025-09-12 18:00:53 -05:00
Hafiz
bd3625aa4b remove redundant disabled styles in task modals
The .disabled class conflicting with existing disabled state implementations
2025-09-12 18:00:53 -05:00
Hafiz
0654d59752 Responsive Layout for Equipment Containers
- Added responsive CSS for mobile (<768px) and tablet (769px-1024px)
- Implemented flex-wrap layout that automatically stacks items in rows of 4 on smaller
2025-09-12 18:00:49 -05:00
Hafiz
0f901c9007 lint fix 2025-09-12 18:00:05 -05:00
Hafiz
30b6584a47 Update ToS error message
- Updated account suspension message from "This account, User ID..." to "Your account @[username] has been
  blocked..."
- Modified server auth middleware to pass username parameter when throwing account suspended error
-Modified auth utils loginRes function to include username in suspended account error
- Updated client bannedAccountModal component to pass username (empty string if unavailable)
- Updated login test to expect username in account suspended message
2025-09-12 18:00:05 -05:00
Hafiz
846250e4b8 Fix shop tabs overflow off screen at certain zoom levels
Fix quest cards get cut off on small screens
Fix pop-up windows extend past screen edges on mobile
2025-09-12 18:00:05 -05:00
Hafiz
0d1a5b6a7c Await genericPurchase completion before page reload to prevent request cancellation.
Also adds defensive check for undefined error.response in axios interceptor to prevent "t.response undefined" errors.
2025-09-12 18:00:05 -05:00
Phillip Thelen
c97845329a lint fixes 2025-09-12 17:56:34 -05:00
Phillip Thelen
5adbf536f5 add blocker to block emails from registration 2025-09-12 17:51:49 -05:00
Phillip Thelen
d9e76fcb3f restructure admin pages 2025-09-12 17:46:26 -05:00
Phillip Thelen
1efe30b7a7 Add UI for managing blockers 2025-09-12 17:36:29 -05:00
Phillip Thelen
4e2a8eb550 Tweak wording 2025-09-12 17:36:29 -05:00
Phillip Thelen
c3fd1fdd66 correctly reset local data after creating blocker 2025-09-12 17:36:29 -05:00
Phillip Thelen
0ec74582f0 Add UI for managing blockers 2025-09-12 17:36:27 -05:00
Phillip Thelen
69bf75322f add new frontend files 2025-09-12 17:33:03 -05:00
9 changed files with 27 additions and 15 deletions

View File

@@ -44,7 +44,7 @@ describe('POST /user/auth/local/login', () => {
})).to.eventually.be.rejected.and.eql({ })).to.eventually.be.rejected.and.eql({
code: 401, code: 401,
error: 'NotAuthorized', error: 'NotAuthorized',
message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id }), message: t('accountSuspended', { communityManagerEmail: nconf.get('EMAILS_COMMUNITY_MANAGER_EMAIL'), userId: user._id, username: user.auth.local.username }),
}); });
}); });

View File

@@ -203,6 +203,9 @@ export default {
return response; return response;
}, error => { // Set up Error interceptors }, error => { // Set up Error interceptors
if (!error.response) {
return Promise.reject(error);
}
if (error.response.status >= 400) { if (error.response.status >= 400) {
const isBanned = this.checkForBannedUser(error); const isBanned = this.checkForBannedUser(error);
if (isBanned === true) return null; // eslint-disable-line consistent-return if (isBanned === true) return null; // eslint-disable-line consistent-return

View File

@@ -43,9 +43,11 @@ export default {
const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY); const AUTH_SETTINGS = localStorage.getItem(LOCALSTORAGE_AUTH_KEY);
const parseSettings = JSON.parse(AUTH_SETTINGS); const parseSettings = JSON.parse(AUTH_SETTINGS);
const userId = parseSettings ? parseSettings.auth.apiId : ''; const userId = parseSettings ? parseSettings.auth.apiId : '';
const username = this.$store?.state?.user?.data?.auth?.local?.username || '';
return this.$t('accountSuspended', { return this.$t('accountSuspended', {
userId, userId,
username,
communityManagerEmail: COMMUNITY_MANAGER_EMAIL, communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
}); });
}, },

View File

@@ -851,7 +851,7 @@ export default {
return; return;
} }
if (this.genericPurchase) { if (this.genericPurchase) {
this.makeGenericPurchase(this.item, 'buyModal', this.selectedAmountToBuy); await this.makeGenericPurchase(this.item, 'buyModal', this.selectedAmountToBuy);
await this.purchased(this.item.text); await this.purchased(this.item.text);
} }
} }

View File

@@ -35,7 +35,7 @@
</button> </button>
<button <button
class="btn btn-secondary d-flex align-items-center justify-content-center" class="btn btn-secondary d-flex align-items-center justify-content-center"
:class="{disabled: !canSave}" :class="{'btn-disabled': !canSave}"
type="button" type="button"
@click="submit()" @click="submit()"
> >
@@ -162,13 +162,13 @@
> >
<div <div
class="habit-option-icon svg-icon no-transition" class="habit-option-icon svg-icon no-transition"
:class="task.up ? '' : 'disabled'" :class="task.up ? '' : 'icon-disabled'"
v-html="icons.positive" v-html="icons.positive"
></div> ></div>
</div> </div>
<div <div
class="habit-option-label no-transition" class="habit-option-label no-transition"
:class="task.up ? cssClass('icon') : 'disabled'" :class="task.up ? cssClass('icon') : 'label-disabled'"
> >
{{ $t('positive') }} {{ $t('positive') }}
</div> </div>
@@ -188,13 +188,13 @@
> >
<div <div
class="habit-option-icon no-transition svg-icon negative mx-auto" class="habit-option-icon no-transition svg-icon negative mx-auto"
:class="task.down ? '' : 'disabled'" :class="task.down ? '' : 'icon-disabled'"
v-html="icons.negative" v-html="icons.negative"
></div> ></div>
</div> </div>
<div <div
class="habit-option-label no-transition" class="habit-option-label no-transition"
:class="task.down ? cssClass('icon') : 'disabled'" :class="task.down ? cssClass('icon') : 'label-disabled'"
> >
{{ $t('negative') }} {{ $t('negative') }}
</div> </div>
@@ -592,7 +592,7 @@
<button <button
class="btn btn-primary btn-footer class="btn btn-primary btn-footer
d-flex align-items-center justify-content-center" d-flex align-items-center justify-content-center"
:class="{disabled: !canSave}" :class="{'btn-disabled': !canSave}"
type="button" type="button"
@click="submit()" @click="submit()"
> >
@@ -881,12 +881,14 @@
} }
} }
.disabled { .btn-disabled {
background-color: $white; background-color: $white;
border: 2px solid transparent; border: 2px solid transparent;
color: $gray-200; color: $gray-200;
line-height: 1.714; line-height: 1.714;
box-shadow: 0px 1px 3px 0px rgba(26, 24, 29, 0.12), 0px 1px 2px 0px rgba(26, 24, 29, 0.24); box-shadow: 0px 1px 3px 0px rgba(26, 24, 29, 0.12), 0px 1px 2px 0px rgba(26, 24, 29, 0.24);
cursor: not-allowed;
opacity: 0.6;
&:focus { &:focus {
background-color: $white; background-color: $white;
@@ -948,7 +950,7 @@
height: 10px; height: 10px;
color: $white; color: $white;
&.disabled { &.icon-disabled {
color: $gray-200; color: $gray-200;
} }
@@ -962,7 +964,7 @@
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
&.disabled { &.label-disabled {
color: $gray-100; color: $gray-100;
font-weight: normal; font-weight: normal;
} }
@@ -1018,7 +1020,7 @@
border: 0; border: 0;
} }
.disabled .input-group-text { .input-group-outer.disabled .input-group-text {
color: $gray-200; color: $gray-200;
} }

View File

@@ -116,7 +116,7 @@
.toggle-switch-inner:before { .toggle-switch-inner:before {
content: ""; content: "";
padding-left: 10px; padding-left: 10px;
background-color: $green-10; background-color: $green-50;
} }
.toggle-switch-inner:after { .toggle-switch-inner:after {

View File

@@ -133,7 +133,7 @@
"passwordReset": "If we have your email or username on file, instructions for setting a new password have been sent to your email.", "passwordReset": "If we have your email or username on file, instructions for setting a new password have been sent to your email.",
"invalidLoginCredentialsLong": "Your email, username, or password are incorrect. Please try again or use \"Forgot Password.\"", "invalidLoginCredentialsLong": "Your email, username, or password are incorrect. Please try again or use \"Forgot Password.\"",
"invalidCredentials": "There is no account that uses those credentials.", "invalidCredentials": "There is no account that uses those credentials.",
"accountSuspended": "This account, User ID \"<%= userId %>\", has been blocked for breaking the Community Guidelines (https://habitica.com/static/community-guidelines) or Terms of Service (https://habitica.com/static/terms). For details or to ask to be unblocked, please email our Community Manager at <%= communityManagerEmail %> or ask your parent or guardian to email them. Please include your @Username in the email.", "accountSuspended": "Your account @<%= username %> has been blocked. For additional information, or to request an appeal, email admin@habitica.com with your Habitica username or User ID.",
"accountSuspendedTitle": "Account has been suspended", "accountSuspendedTitle": "Account has been suspended",
"unsupportedNetwork": "This network is not currently supported.", "unsupportedNetwork": "This network is not currently supported.",
"cantDetachSocial": "Account lacks another authentication method; can't detach this authentication method.", "cantDetachSocial": "Account lacks another authentication method; can't detach this authentication method.",

View File

@@ -17,7 +17,11 @@ export function loginRes (user, req, res) {
if (user.auth.blocked) { if (user.auth.blocked) {
throw new NotAuthorized(res.t( throw new NotAuthorized(res.t(
'accountSuspended', 'accountSuspended',
{ communityManagerEmail: COMMUNITY_MANAGER_EMAIL, userId: user._id }, {
communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
userId: user._id,
username: user.auth.local.username,
},
)); ));
} }
const urlPath = url.parse(req.url).pathname; const urlPath = url.parse(req.url).pathname;

View File

@@ -100,6 +100,7 @@ export function authWithHeaders (options = {}) {
throw new NotAuthorized(common.i18n.t('accountSuspended', { throw new NotAuthorized(common.i18n.t('accountSuspended', {
communityManagerEmail: COMMUNITY_MANAGER_EMAIL, communityManagerEmail: COMMUNITY_MANAGER_EMAIL,
userId: user._id, userId: user._id,
username: user.auth.local.username,
}, language)); }, language));
} }