Skip to content

New page access denied page instead of something went wrong #166

Merged
Rajat-Dabade merged 10 commits intomainfrom
new-page-access-denied
Feb 18, 2026
Merged

New page access denied page instead of something went wrong #166
Rajat-Dabade merged 10 commits intomainfrom
new-page-access-denied

Conversation

@Rajat-Dabade
Copy link
Contributor

@Rajat-Dabade Rajat-Dabade commented Feb 3, 2026

Summary

If do not have board access

Before
Visiting the board that you don't have access to:
Screenshot 2026-02-03 at 6 31 02 PM

After:
Screenshot 2026-02-03 at 6 28 05 PM

Ticket Link

https://mattermost.atlassian.net/browse/MM-67078

Summary by CodeRabbit

  • New Features
    • Added a dedicated Access Denied page with illustration and a "Back to your boards" button for private or restricted boards.
  • Improvements
    • Centralized permission/error handling to surface clearer messaging and preserve navigation state when access is denied.
  • Bug Fixes
    • Ensured 403/forbidden responses consistently trigger the Access Denied flow and prevent accidental rendering of restricted content.

@Rajat-Dabade Rajat-Dabade self-assigned this Feb 3, 2026
@Rajat-Dabade Rajat-Dabade added 1: UX Review Requires review by a UX Designer 2: Dev Review Requires review by a core committer 3: QA Review Requires review by a QA tester labels Feb 3, 2026
@marianunez
Copy link
Member

@coderabbitai review this PR

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

@marianunez: I'll review this PR for you. The change improves the user experience by replacing the generic "Sorry, something went wrong" error with a more specific "You don't have access to this board" message when users attempt to access boards they don't have permissions for.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

This PR introduces an "access denied" error handling feature. It adds a new AccessDeniedPage component displayed when users lack board access permissions. Changes include new i18n keys, an ErrorId enum member, API error handling for 403 responses, error detection in load operations, router redirection logic, and related UI components.

Changes

Cohort / File(s) Summary
Internationalization
webapp/i18n/en.json
Added three new i18n keys for access denied page: accessDenied.page.title, accessDenied.page.subtitle, and accessDenied.back-to-home.
Error Type Definition
webapp/src/errors.ts
Added AccessDenied = 'access-denied' enum member to ErrorId.
API & Error Detection
webapp/src/octoClient.ts, webapp/src/store/initialLoad.ts
Enhanced API error handling: octoClient.ts throws AccessDenied error on 403 responses; initialLoad.ts maps 403 status and access-denied messages to AccessDenied error type.
Error State Management
webapp/src/store/globalError.ts
Extended globalError slice to handle loadBoardData rejections, preserving error message in global state.
Access Denied Page UI
webapp/src/pages/accessDeniedPage.tsx, webapp/src/pages/accessDeniedPage.scss, webapp/src/svg/access-denied-illustation.tsx
New AccessDeniedPage component with styling and SVG illustration. Renders localized title/subtitle, illustration, and "Back to your boards" button that clears errors and navigates home.
Page & Routing
webapp/src/pages/boardPage/boardPage.tsx, webapp/src/router.tsx
BoardPage adds error handling for access-denied scenarios with try/catch around join/load operations. Router extends with /access-denied route and conditional redirect logic for AccessDenied and InvalidReadOnlyBoard errors.

Sequence Diagram

sequenceDiagram
    actor User
    participant BoardPage
    participant octoClient
    participant API
    participant Store as Store/<br/>Global Error
    participant Router
    participant AccessDeniedPage

    User->>BoardPage: Attempt to access board
    BoardPage->>octoClient: joinBoard(boardId)
    octoClient->>API: Request board access
    API-->>octoClient: 403 Forbidden
    octoClient->>octoClient: Throw error<br/>(message: 'access-denied'<br/>status: 403)
    octoClient-->>BoardPage: Error thrown
    BoardPage->>Store: Dispatch setGlobalError('access-denied')
    Store->>Router: Global error state updated
    Router->>Router: Detect AccessDenied error
    Router->>AccessDeniedPage: Redirect to /access-denied
    AccessDeniedPage->>User: Render access denied page<br/>with illustration & button
    User->>AccessDeniedPage: Click "Back to your boards"
    AccessDeniedPage->>Store: Clear global error
    AccessDeniedPage->>Router: Navigate to /
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly reflects the main change: introducing a new access-denied page to replace a generic error message for users without board access.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch new-page-access-denied

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@webapp/src/pages/boardPage/boardPage.tsx`:
- Around line 218-229: In the access-denied branch of the catch block (where
err?.status === 403 or err?.message === 'access-denied'), clear the stored
lastBoardID and lastViewId the same way the 'board-not-found' branch does to
avoid TeamToBoardAndViewRedirect sending users back to an unauthorized board; if
the user is not an admin (i.e., after checking myUser.permissions and before
calling dispatch(setGlobalError(ErrorId.AccessDenied))), dispatch the same
actions or invoke the same logic used to clear lastBoardID and lastViewId so the
redirect loop is prevented.
🧹 Nitpick comments (3)
webapp/src/store/initialLoad.ts (1)

68-79: Consider centralizing access-denied detection to avoid message heuristics.

Relying on message substrings is brittle. If feasible, normalize access-denied errors in the client (status/code) and key off that here.

webapp/src/svg/access-denied-illustation.tsx (1)

6-46: Mark the decorative SVG as aria-hidden.

This avoids screen readers announcing a non-informative illustration.

♿ Suggested accessibility tweak
-            <svg width="400" height="290" viewBox="0 0 347 213" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <svg width="400" height="290" viewBox="0 0 347 213" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
webapp/src/pages/accessDeniedPage.tsx (1)

29-32: Consider hiding the decorative illustration from assistive tech.
If the SVG is purely decorative, wrapping it with aria-hidden='true' avoids screen‑reader noise.

♿ Suggested tweak
-                <AccessDeniedIllustration/>
+                <span aria-hidden='true'>
+                    <AccessDeniedIllustration/>
+                </span>

Copy link

@matthewbirtch matthewbirtch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just need to make some tweaks to support themes better @Rajat-Dabade

Copy link
Member

@harshilsharma63 harshilsharma63 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

The image and text color looks incorrect on a dark theme.

@Rajat-Dabade
Copy link
Contributor Author

@harshilsharma63 Fix the dark theme issue

Screenshot 2026-02-05 at 1 58 40 PM Screenshot 2026-02-05 at 1 59 44 PM

@yasserfaraazkhan yasserfaraazkhan added the Setup Cloud Test Server Setup a test server using Mattermost Cloud label Feb 9, 2026
@mm-cloud-bot
Copy link

Plugin Spinwick PR #166 🎉

Test server created!

Access here: https://boards-pr-166-mcgzq.test.mattermost.cloud

Plugin Version Artifact
boards f4fdfae Download

⚠️ Plugin Installation Issue

The test server was created successfully, but there was an issue installing or enabling the plugin automatically:

  • Install Error: failed to install plugin via mmctl: failed with status code 502

You can manually install the plugin:

  1. Download the plugin artifact from the link above
  2. Upload it to your test server at yhd8qbigcpbjdfcguz6fekhotw
  3. Enable it in System Console > Plugins

Future commits will still attempt to automatically update the plugin.

Installation ID: yhd8qbigcpbjdfcguz6fekhotw
Logs: Click here

Credentials: Posted securely in this Mattermost channel - Look for PR #166

@Rajat-Dabade
Copy link
Contributor Author

@harshilsharma63 Just a friendly reminder for reviewing. Thank you.

@mm-cloud-bot
Copy link

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit e618067f0ed17373e32aa962dc436691a62650e3

Plugin Version Artifact
boards e618067 Download

@mm-cloud-bot
Copy link

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit 3d5f980fd3d38ddbcce0c725f65afb6c90f6146b

Plugin Version Artifact
boards 3d5f980 Download

Copy link
Member

@harshilsharma63 harshilsharma63 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with one suggestion.


const handleBackToHome = useCallback(() => {
// Clear any error state before navigating
dispatch(setGlobalError(''))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we, as precaution, clear out the last board and view IDs here? What happens if local storage contains last board/view ID of a board user doesn't have access to. I know we are clearing those before sending the user to this page, but maybe just to be extra careful, we could clear them here as well.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@webapp/src/pages/boardPage/boardPage.tsx`:
- Around line 238-244: In loadOrJoinBoard, don't unconditionally return on a
loadBoardData rejection; instead detect an access-denied rejection and rethrow
it so upstream can handle clearing last-viewed IDs (mirror the earlier pattern):
after awaiting dispatch(loadBoardData(boardId)) check if result.type ===
loadBoardData.rejected.type and if result.payload?.type === 'access-denied' then
throw result.payload (or return Promise.reject(result.payload)); otherwise keep
the existing early return for other errors. Target symbols: loadOrJoinBoard and
loadBoardData.

@mm-cloud-bot
Copy link

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit 1842680b8c70fe6c8e2a9b1853cfb054444121f1

Plugin Version Artifact
boards 1842680 Download

@mm-cloud-bot
Copy link

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit 7ffa07730ece11c09a3a87a221298dfbe933b1d3

Plugin Version Artifact
boards 7ffa077 Download

@yasserfaraazkhan yasserfaraazkhan removed the Setup Cloud Test Server Setup a test server using Mattermost Cloud label Feb 16, 2026
@mm-cloud-bot
Copy link

Test server destroyed

@yasserfaraazkhan
Copy link

/update-branch

@yasserfaraazkhan yasserfaraazkhan added the Setup Cloud Test Server Setup a test server using Mattermost Cloud label Feb 16, 2026
@mm-cloud-bot
Copy link

Plugin Spinwick PR #166 🎉

Test server created!

Access here: https://boards-pr-166-m5agz.test.mattermost.cloud

Plugin Version Artifact
boards fed57cd Download

⚠️ Plugin Installation Issue

The test server was created successfully, but there was an issue installing or enabling the plugin automatically:

  • Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

You can manually install the plugin:

  1. Download the plugin artifact from the link above
  2. Upload it to your test server at w3f3fq3zkpg9tjtbqme9z7diec
  3. Enable it in System Console > Plugins

Future commits will still attempt to automatically update the plugin.

Installation ID: w3f3fq3zkpg9tjtbqme9z7diec
Logs: Click here

Credentials: Posted securely in this Mattermost channel - Look for PR #166

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
webapp/src/pages/boardPage/boardPage.tsx (1)

238-269: ⚠️ Potential issue | 🟡 Minor

Missing dependencies in useCallback could cause stale closure issues.

The loadOrJoinBoard callback has an empty dependency array [] but references joinBoard, dispatch, and setShowJoinBoardDialog which could become stale. While dispatch is stable from Redux, joinBoard is defined inline and captures teamId and other state.

🛠️ Suggested fix
-    }, [])
+    }, [dispatch, joinBoard])

Note: Since joinBoard is not memoized with useCallback, this may cause frequent re-creation of loadOrJoinBoard. Consider also wrapping joinBoard in useCallback if performance becomes a concern.

🤖 Fix all issues with AI agents
In `@webapp/src/pages/boardPage/boardPage.tsx`:
- Around line 295-301: The effect watching match.params.boardId and currentBoard
can call loadOrJoinBoard twice on mount; add a guard to only treat the case as
"board was removed" (not "not yet loaded") by tracking previous board presence
and only calling loadOrJoinBoard when a board that previously existed is now
missing: use a ref like prevBoardIdRef or prevHasBoardRef to capture prior
currentBoard (or its id) and in the useEffect check that prevBoardIdRef.current
!== undefined && currentBoard === undefined && match.params.boardId &&
!props.readonly && me before invoking loadOrJoinBoard; update the ref after
render so future changes behave normally (references: useEffect,
loadOrJoinBoard, match.params.boardId, props.readonly, me, currentBoard).
🧹 Nitpick comments (3)
webapp/src/pages/accessDeniedPage.tsx (1)

75-75: Prefer CSS for spacing instead of <br/>.

Using <br/> for visual spacing is less maintainable than CSS margin/padding. Consider adding margin to the .subtitle or button container in the SCSS file instead.

♻️ Suggested fix
-                <br/>
                 <Button

Then add spacing in accessDeniedPage.scss:

.AccessDeniedPage .subtitle {
    margin-bottom: 1.5rem; /* or appropriate value */
}
webapp/src/pages/boardPage/boardPage.tsx (2)

254-259: Empty catch block silently swallows errors.

While the comment states errors are handled in joinBoard, any unexpected errors (e.g., network failures not caught by the inner try/catch, or errors thrown after the catch block) would be silently swallowed. Consider logging unexpected errors for debugging.

♻️ Optional improvement
             try {
                 await joinBoard(myUser, boardTeamId, boardId, false)
             } catch (error: unknown) {
-                // Error already handled in joinBoard
+                // Most errors are handled in joinBoard, but log unexpected ones
+                Utils.log('Unexpected error in loadOrJoinBoard:', error)
             }

200-236: Use ErrorId.BoardNotFound enum instead of string literal for consistency.

The function uses the string literal 'board-not-found' at lines 210 and 233, while line 231 correctly uses the ErrorId.AccessDenied enum. Since ErrorId.BoardNotFound is already defined in errors.ts, update both occurrences to use the enum for consistency and type safety.

♻️ Suggested fix
                 UserSettings.setLastBoardID(boardTeamId, null)
                 UserSettings.setLastViewId(boardId, null)
-                dispatch(setGlobalError('board-not-found'))
+                dispatch(setGlobalError(ErrorId.BoardNotFound))
                 return
             }

And similarly at line 233:

             } else {
-                dispatch(setGlobalError('board-not-found'))
+                dispatch(setGlobalError(ErrorId.BoardNotFound))
             }

@Rajat-Dabade
Copy link
Contributor Author

@yasserfaraazkhan, a friendly reminder for the review.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
webapp/src/pages/boardPage/boardPage.tsx (2)

242-254: Non-access-denied rejections are silently ignored.

When loadBoardData is rejected with an error other than AccessDenied, the code returns early without any error handling (line 253). This could leave users in an unclear state if the rejection is due to network issues or other transient errors.

Consider handling other rejection types explicitly:

♻️ Proposed improvement for error handling
     if (result.type === loadBoardData.rejected.type) {
         if (result.error?.message === ErrorId.AccessDenied) {
             const isAdmin = myUser.permissions?.find((s) => s === 'manage_system' || s === 'manage_team')
             if (isAdmin) {
                 setShowJoinBoardDialog(true)
                 return
             }
             UserSettings.setLastBoardID(boardTeamId, null)
             UserSettings.setLastViewId(boardId, null)
             dispatch(setGlobalError(ErrorId.AccessDenied))
+        } else {
+            // Handle other errors (network issues, etc.)
+            dispatch(setGlobalError('board-not-found'))
         }
         return
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/pages/boardPage/boardPage.tsx` around lines 242 - 254, The current
branch for loadBoardData.rejected only handles ErrorId.AccessDenied and silently
returns for all other rejections; update the rejection handling in the block
that checks result.type === loadBoardData.rejected.type to add an else path that
handles non-access-denied errors (e.g., dispatch a generic/global error via
dispatch(setGlobalError(...)), log the error, and/or show a user-facing message)
while keeping the existing AccessDenied flow (isAdmin check,
setShowJoinBoardDialog, UserSettings.setLastBoardID/setLastViewId). Use
result.error?.message or a fallback ErrorId/Unknown value when calling
setGlobalError so transient/network failures aren’t ignored.

255-261: Empty catch block swallows potential errors.

The catch block at lines 258-260 is empty with a comment stating errors are handled in joinBoard. However, if joinBoard throws an unexpected error not handled internally, it will be silently swallowed.

Consider logging unexpected errors for debugging:

♻️ Optional improvement
         try {
             await joinBoard(myUser, boardTeamId, boardId, false)
         } catch (error: unknown) {
-            // Error already handled in joinBoard
+            // Most errors handled in joinBoard, log unexpected ones
+            Utils.logError('Unexpected error in joinBoard:', error)
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/pages/boardPage/boardPage.tsx` around lines 255 - 261, The empty
catch in the block that calls joinBoard(myUser, boardTeamId, boardId, false)
swallows unexpected errors; update the catch to at minimum log the error
(including context like myUser.id, boardId, boardTeamId, and
result.payload?.blocks length) using the existing logger or console.error, and
consider rethrowing or forwarding the error if it should bubble up; ensure you
reference the joinBoard call and preserve the comment that joinBoard handles
known errors while still surfacing any unexpected exceptions.
webapp/src/router.tsx (1)

180-197: Duplicate dispatch(setGlobalError('')) calls in both branches.

The global error is cleared in both the early-return guard (line 184) and before the redirect (line 188). While this works correctly, the second dispatch at line 188 could be moved after the conditional to avoid redundancy.

♻️ Optional refactor to reduce duplication
     useEffect(() => {
         if (globalError) {
             // Don't redirect if we're already on an error page
             if (location.pathname === '/error' || location.pathname === '/access-denied') {
                 dispatch(setGlobalError(''))
                 return
             }

-            dispatch(setGlobalError(''))
             // Redirect to access denied page for access denied errors
             if (globalError === ErrorId.AccessDenied || globalError === ErrorId.InvalidReadOnlyBoard) {
                 const currentPath = location.pathname + location.search
+                dispatch(setGlobalError(''))
                 history.replace(`/access-denied?r=${encodeURIComponent(currentPath)}`)
             } else {
+                dispatch(setGlobalError(''))
                 history.replace(`/error?id=${globalError}`)
             }
         }
     }, [globalError, history, location, dispatch])

Alternatively, keep as-is since the current structure is clear about always clearing before navigation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@webapp/src/router.tsx` around lines 180 - 197, The useEffect handling
globalError calls dispatch(setGlobalError('')) twice (once in the early-return
guard when location.pathname is '/error' or '/access-denied', and again before
redirecting); refactor by removing the duplicate call and performing a single
dispatch(setGlobalError('')) after the conditional guard so the error is cleared
exactly once before any navigation; update the useEffect that references
globalError, history, location, and setGlobalError to call
dispatch(setGlobalError('')) in one place (outside/below the if/else redirect
logic) and keep the existing redirects for ErrorId.AccessDenied /
ErrorId.InvalidReadOnlyBoard and the generic error case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@webapp/src/pages/boardPage/boardPage.tsx`:
- Around line 242-254: The current branch for loadBoardData.rejected only
handles ErrorId.AccessDenied and silently returns for all other rejections;
update the rejection handling in the block that checks result.type ===
loadBoardData.rejected.type to add an else path that handles non-access-denied
errors (e.g., dispatch a generic/global error via dispatch(setGlobalError(...)),
log the error, and/or show a user-facing message) while keeping the existing
AccessDenied flow (isAdmin check, setShowJoinBoardDialog,
UserSettings.setLastBoardID/setLastViewId). Use result.error?.message or a
fallback ErrorId/Unknown value when calling setGlobalError so transient/network
failures aren’t ignored.
- Around line 255-261: The empty catch in the block that calls joinBoard(myUser,
boardTeamId, boardId, false) swallows unexpected errors; update the catch to at
minimum log the error (including context like myUser.id, boardId, boardTeamId,
and result.payload?.blocks length) using the existing logger or console.error,
and consider rethrowing or forwarding the error if it should bubble up; ensure
you reference the joinBoard call and preserve the comment that joinBoard handles
known errors while still surfacing any unexpected exceptions.

In `@webapp/src/router.tsx`:
- Around line 180-197: The useEffect handling globalError calls
dispatch(setGlobalError('')) twice (once in the early-return guard when
location.pathname is '/error' or '/access-denied', and again before
redirecting); refactor by removing the duplicate call and performing a single
dispatch(setGlobalError('')) after the conditional guard so the error is cleared
exactly once before any navigation; update the useEffect that references
globalError, history, location, and setGlobalError to call
dispatch(setGlobalError('')) in one place (outside/below the if/else redirect
logic) and keep the existing redirects for ErrorId.AccessDenied /
ErrorId.InvalidReadOnlyBoard and the generic error case.

@mm-cloud-bot
Copy link

Plugin test server update attempted, but encountered an issue:

⚠️ Install Error: failed to wait for S3 artifact: timed out waiting for S3 artifact

The test server is still available. You can manually download and install the updated plugin using the artifact link below.

Updated with git commit cd0b61b5a2ab59b8ce646fcd68fcb1e78be44f4a

Plugin Version Artifact
boards cd0b61b Download

@marianunez marianunez removed 1: UX Review Requires review by a UX Designer 2: Dev Review Requires review by a core committer labels Feb 18, 2026
@Rajat-Dabade Rajat-Dabade merged commit bc67146 into main Feb 18, 2026
15 checks passed
@mm-cloud-bot
Copy link

Test server destroyed

@Rajat-Dabade Rajat-Dabade removed the 3: QA Review Requires review by a QA tester label Feb 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Setup Cloud Test Server Setup a test server using Mattermost Cloud

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants