Skip to content

feat: Improve manual license management - #40916

Merged
ggazzo merged 15 commits into
developfrom
feat/improve-manual-license
Jul 6, 2026
Merged

feat: Improve manual license management#40916
ggazzo merged 15 commits into
developfrom
feat/improve-manual-license

Conversation

@dougfabris

@dougfabris dougfabris commented Jun 11, 2026

Copy link
Copy Markdown
Member

Proposed changes (including videos or screenshots)

Adds a Manage License flow to the Subscription page so admins can preview a license before applying it, and moves license management out of the Enterprise settings page.

What's new:

  • ManageLicenseModal — paste, upload (.txt), or drag-and-drop a license key. The key is validated in real time (debounced) so the admin can confirm it's valid before applying. Supports applying a new license and removing the current one (with a confirmation step).
  • Specific validation messages — rejected licenses map to actionable copy (expired, URL mismatch, limits exceeded, out of date range, undecodable) instead of a generic error.
  • License controls on the Plan card — shows Site URL, Hashed Site URL, and the license key with a manage/add action.
  • Enterprise settings page now redirects to the Subscription page, where license management lives.

How to test

  • Go to Admin → Subscription.
  • On the Plan card, use Add license / the manage (cog) action.
  • Paste, upload, or drop a license — confirm the status updates (valid / specific error) and that Apply is only enabled for a valid, non-current license.
  • Remove the current license and confirm the community-license flow.

Issue(s)

Steps to test or reproduce

Further comments

CORE-2104

Summary by CodeRabbit

  • New Features

    • Added a new license management flow in Subscription settings, including upload, validation, apply, and remove actions.
    • Added license file support with drag-and-drop, text input, and file preview.
    • Workspace details now show site URLs and license information with copy-to-clipboard actions.
    • The Enterprise settings area now directs users to manage licenses from Subscription settings.
  • Bug Fixes

    • Improved license validation feedback with clearer messages for invalid, expired, and unsupported licenses.

rodrigok and others added 2 commits June 9, 2026 14:08
…ying

Adds a new `POST /api/v1/licenses.validate` REST endpoint that validates a
Rocket.Chat license (V2 or V3 JWT) against the current workspace's validation
structure without applying it, so the result can be previewed from the UI
before the license is committed.

- core-typings: new `LicenseValidationResult` type
- license: `LicenseManager.validateLicenseForPreview()` runs the same
  validation pipeline used on apply (URL, periods, limits) without mutating
  state or emitting events; the shared `licenseValidationBehaviors` constant
  is reused by both the apply and preview paths to avoid duplication
- rest-typings: `isLicensesValidateProps` schema + endpoint typing
- meteor: `licenses.validate` route (edit-privileged-setting)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dionisio-bot

dionisio-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 40be9c4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@rocket.chat/i18n Minor
@rocket.chat/meteor Minor
@rocket.chat/core-typings Minor
@rocket.chat/rest-typings Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dougfabris dougfabris added this to the 8.6.0 milestone Jun 11, 2026
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds a "manage license" flow to the subscription admin page: users can view, validate, apply, or remove an enterprise license via a new modal with drag-and-drop/text input, validation feedback, and confirmation prompts. The Enterprise settings page now redirects to the subscription page. Server info query logic was refactored into a reusable hook, plan card components were updated to display license details, and corresponding i18n strings and tests were added.

Changes

License Management Feature

Layer / File(s) Summary
Server info query hook
apps/meteor/client/hooks/useWorkspaceInfo.ts
Extracts server info query options into useServerInfoQueryOptions/useServerInfo and wires it into useWorkspaceInfo.
Enterprise settings redirect
apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx, apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx
New page renders a message and redirect link to /admin/subscription; selector routes Enterprise group to it.
License validation and messaging
.../hooks/useValidateLicense.ts, .../ManageLicenseModal/getLicenseInvalidMessage.ts, related specs
Adds useValidateLicense hook calling licenses.validate, isPlausibleLicense gate, and getLicenseInvalidMessage reason-to-message mapping.
License file input hook
.../ManageLicenseModal/useLicenseFileInput.ts, LicenseFilePreview.tsx, specs
Adds hook for drag/drop/text license input with .txt validation, plus a file preview component.
Manage license modal
.../ManageLicenseModal/ManageLicenseModal.tsx, LicenseStatus.tsx, index.ts, specs
Implements apply/remove license modal with validation status display, confirmation flow for removal, and toast notifications.
Plan card integration
.../PlanCard.tsx, PlanCard/PlanCardLicenseDetails.tsx, PlanCardCommunity.tsx, PlanCardPremium.tsx, PlanCardTrial.tsx, SubscriptionPage.tsx, FeaturesCard.tsx
Adds license details display (site URL, hashed URL, license key, manage/add actions) across plan cards; PlanCard now accepts license/licenseLimits; SubscriptionPage always renders PlanCard.
i18n strings and changeset
packages/i18n/src/locales/en.i18n.json, .changeset/silver-cars-kneel.md
Adds new translation keys for license workflows and a minor version changeset.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ManageLicenseModal
  participant useLicenseFileInput
  participant useValidateLicense
  participant SettingsAPI

  User->>ManageLicenseModal: Enter/upload license text
  ManageLicenseModal->>useLicenseFileInput: handle text/file input
  ManageLicenseModal->>useValidateLicense: validate trimmed license
  useValidateLicense-->>ManageLicenseModal: valid/invalid + reasons
  User->>ManageLicenseModal: Click Apply license
  ManageLicenseModal->>SettingsAPI: update Enterprise_License setting
  SettingsAPI-->>ManageLicenseModal: success/error
  ManageLicenseModal-->>User: toast + close modal
Loading
sequenceDiagram
  participant User
  participant SettingsGroupSelector
  participant EnterpriseGroupPage
  participant Router

  User->>SettingsGroupSelector: Open Enterprise settings group
  SettingsGroupSelector->>EnterpriseGroupPage: Render page (groupId === 'Enterprise')
  User->>EnterpriseGroupPage: Click subscription link
  EnterpriseGroupPage->>Router: navigate('/admin/subscription')
Loading

Possibly related PRs

  • RocketChat/Rocket.Chat#40685: Main PR's license UI relies on serverInfo including the hashed workspace URL, matching this retrieved PR's addition of workspaceUrl/hashedWorkspaceUrl to getServerInfo.
  • RocketChat/Rocket.Chat#40909: The new manage-license modal updates the Enterprise_License setting via the shared settings dispatch flow modified by this PR.

Suggested labels: type: feature

Suggested reviewers: ggazzo, abhinavkrin

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main change: improved manual license management.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • CORE-2104: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch from 1acd309 to caf79ce Compare June 11, 2026 21:31
@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.33624% with 58 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.21%. Comparing base (70c0ff0) to head (40be9c4).
⚠️ Report is 57 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40916      +/-   ##
===========================================
+ Coverage    69.10%   69.21%   +0.11%     
===========================================
  Files         3433     3440       +7     
  Lines       132340   132782     +442     
  Branches     23104    23265     +161     
===========================================
+ Hits         91452    91909     +457     
+ Misses       37520    37512       -8     
+ Partials      3368     3361       -7     
Flag Coverage Δ
e2e 59.35% <37.50%> (-0.01%) ⬇️
e2e-api 40.44% <ø> (+0.04%) ⬆️
unit 70.10% <90.09%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch from caf79ce to 4ca7893 Compare June 17, 2026 12:51
@alfredodelfabro alfredodelfabro modified the milestones: 8.6.0, 8.7.0 Jun 17, 2026
@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch from 4ca7893 to 7920406 Compare June 25, 2026 18:07
ggazzo and others added 2 commits June 30, 2026 18:43
Reworks the licenses.validate endpoint so a valid license responds with a
plain success and an invalid one responds with the validation behaviors that
rejected it, instead of always returning a full validation result object.

- license: validateLicenseForPreview now returns the invalidating
  BehaviorWithContext[] (empty means accepted), guards with
  isReadyForValidation, and throws InvalidLicenseError on a malformed string,
  mirroring the apply path and reusing licenseValidationBehaviors/runValidation
- meteor: route returns API.v1.success() when valid, API.v1.failure() with the
  reasons when not
- rest-typings: licenses.validate POST now returns void
- core-typings: drop the now-unused LicenseValidationResult type

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch 3 times, most recently from c4bdea3 to f680c49 Compare July 2, 2026 20:28
ggazzo and others added 4 commits July 2, 2026 20:57
A malformed license string is now reported as invalid with no reasons instead
of throwing, so licenses.validate answers every rejected license with the same
`{ error: 'license-invalid', reasons }` envelope. validateLicenseForPreview
returns a `{ valid: true } | { valid: false; reasons }` result; it still throws
NotReadyForValidation while the workspace can't validate yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch from f680c49 to a0c6663 Compare July 3, 2026 14:14
@dougfabris
dougfabris changed the base branch from develop to worktree-majestic-wibbling-jellyfish July 3, 2026 18:21
@dougfabris
dougfabris force-pushed the feat/improve-manual-license branch from 9b49e40 to 40be9c4 Compare July 3, 2026 20:55
@dougfabris
dougfabris marked this pull request as ready for review July 4, 2026 12:18
@dougfabris
dougfabris requested a review from a team as a code owner July 4, 2026 12:18
@coderabbitai coderabbitai Bot added the type: feature Pull requests that introduces new feature label Jul 4, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 22 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts">

<violation number="1" location="apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts:45">
P2: The modal can end up applying the wrong license when two files are selected in quick succession, because asynchronous file reads are not ordered before updating `license`. Guarding the state update to only apply the latest pending file read would keep `selectedFile` and `license` consistent.</violation>
</file>

<file name="apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx">

<violation number="1" location="apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx:72">
P2: Applying can store a different payload than the one that was validated, because the modal validates `trimmedLicense` but saves raw `license`. Persisting `trimmedLicense` here keeps saved data consistent with validation and avoids whitespace-caused invalid license states.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

setFileError(undefined);
setSelectedFile(selected);
try {
setLicense(await readFileAsText(selected));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: The modal can end up applying the wrong license when two files are selected in quick succession, because asynchronous file reads are not ordered before updating license. Guarding the state update to only apply the latest pending file read would keep selectedFile and license consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts, line 45:

<comment>The modal can end up applying the wrong license when two files are selected in quick succession, because asynchronous file reads are not ordered before updating `license`. Guarding the state update to only apply the latest pending file read would keep `selectedFile` and `license` consistent.</comment>

<file context>
@@ -0,0 +1,106 @@
+		setFileError(undefined);
+		setSelectedFile(selected);
+		try {
+			setLicense(await readFileAsText(selected));
+		} catch {
+			setSelectedFile(undefined);
</file context>


const handleApply = async () => {
try {
await setEnterpriseLicense(license);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Applying can store a different payload than the one that was validated, because the modal validates trimmedLicense but saves raw license. Persisting trimmedLicense here keeps saved data consistent with validation and avoids whitespace-caused invalid license states.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx, line 72:

<comment>Applying can store a different payload than the one that was validated, because the modal validates `trimmedLicense` but saves raw `license`. Persisting `trimmedLicense` here keeps saved data consistent with validation and avoids whitespace-caused invalid license states.</comment>

<file context>
@@ -0,0 +1,180 @@
+
+	const handleApply = async () => {
+		try {
+			await setEnterpriseLicense(license);
+			invalidateLicense(100);
+			onCancel();
</file context>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx (1)

20-20: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

|| true makes the sales-assisted branch unconditional. In apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx:20, licenseInformation.grantedBy?.method !== 'self-service' || true always evaluates to true, so the self-service message and button path can never render. Remove the || true or replace it with the intended condition.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx`
at line 20, The isSalesAssisted condition in PlanCardTrial is currently
unconditional because of the trailing || true, which prevents the self-service
branch from ever rendering. Update the conditional in PlanCardTrial to use the
intended check on licenseInformation.grantedBy?.method without forcing it true,
so the component can correctly switch between sales-assisted and self-service
behavior.
🧹 Nitpick comments (4)
apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Comments present in implementation code.

As per coding guidelines, **/*.{ts,tsx,js} should "Avoid code comments in the implementation." Lines 5 and 10 add explanatory comments that could be avoided by using clearer names (e.g., a well-named constant/type guard).

Also applies to: 10-10

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts` at
line 5, The implementation in useValidateLicense contains explanatory comments
that should be removed and replaced with clearer code structure. Update the
logic around the license length check and the type guard so the intent is
obvious from symbol names and control flow, using a well-named constant or
helper in useValidateLicense instead of inline comments. Keep the behavior
unchanged, but eliminate the comments at the referenced spots and make the
surrounding code self-explanatory.

Source: Coding guidelines

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx (1)

134-136: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Comment present in implementation code.

As per coding guidelines, **/*.{ts,tsx,js} should "Avoid code comments in the implementation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx`
around lines 134 - 136, The implementation in ManageLicenseModal still contains
an explanatory code comment that violates the no-comments guideline. Remove the
inline comment in the file input change handler inside ManageLicenseModal so the
logic remains unchanged while the implementation code no longer includes a
comment; use the surrounding onChange handler and event.currentTarget.value
reset to locate it.

Source: Coding guidelines

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx (1)

9-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Comments present in test implementation.

As per coding guidelines, **/*.{ts,tsx,js} should "Avoid code comments in the implementation." Several explanatory comments are present (e.g., Lines 9, 16, 20, 90, 119); consider expressing this context via more descriptive test/variable names instead.

Also applies to: 16-16, 20-20, 90-90, 119-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx`
at line 9, The ManageLicenseModal.spec.tsx test file contains explanatory
implementation comments that should be removed per the coding guidelines. Update
the relevant tests in ManageLicenseModal.spec.tsx to rely on clearer test names,
variable names, and structure instead of inline comments around the license
length/setup and other annotated steps. Keep the intent of those cases in the
spec, but express it through the existing test blocks and symbols like
ManageLicenseModal and its related validation scenarios without adding comment
text.

Source: Coding guidelines

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx (1)

29-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate "copy field" markup for Site URL and Hashed Site URL.

Both blocks repeat the same label/copy-button/truncated-value structure. Extracting a small reusable sub-component would reduce duplication and simplify adding similar fields later.

♻️ Proposed extraction
+const CopyableField = ({ label, value }: { label: string; value: string }) => {
+	const { t } = useTranslation();
+	const { copy, hasCopied } = useClipboard(value);
+	return (
+		<Box>
+			<Box display='flex'>
+				<Box mie={4}>{label}</Box>
+				{hasCopied ? (
+					<IconButton success icon='check' mini />
+				) : (
+					<IconButton title={t('Copy')} icon='clipboard' mini onClick={() => copy()} />
+				)}
+			</Box>
+			<Box withTruncatedText fontScale='p2'>
+				{value}
+			</Box>
+		</Box>
+	);
+};

 const PlanCardLicenseDetails = () => {
 	...
-	const { copy: copySiteURL, hasCopied: hasCopiedSiteURL } = useClipboard(siteURL);
-	const { copy: copyHashed, hasCopied: hasCopiedHashed } = useClipboard(hashedSiteURL);
 	...
-			<Box>
-				<Box display='flex'>
-					<Box mie={4}>{t('Site_Url')}</Box>
-					{hasCopiedSiteURL ? (
-						<IconButton success icon='check' mini />
-					) : (
-						<IconButton title={t('Copy')} icon='clipboard' mini onClick={() => copySiteURL()} />
-					)}
-				</Box>
-				<Box withTruncatedText fontScale='p2'>
-					{siteURL}
-				</Box>
-			</Box>
-			<Box>
-				<Box display='flex'>
-					<Box mie={4}>{t('Hashed_Site_Url')}</Box>
-					{hasCopiedHashed ? (
-						<IconButton success icon='check' mini />
-					) : (
-						<IconButton title={t('Copy')} icon='clipboard' mini onClick={() => copyHashed()} />
-					)}
-				</Box>
-				<Box withTruncatedText fontScale='p2'>
-					{hashedSiteURL}
-				</Box>
-			</Box>
+			<CopyableField label={t('Site_Url')} value={siteURL} />
+			<CopyableField label={t('Hashed_Site_Url')} value={hashedSiteURL} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx`
around lines 29 - 54, The Site URL and Hashed Site URL sections in
PlanCardLicenseDetails repeat the same label/copy-button/value layout, so
extract that shared UI into a small reusable sub-component (for example inside
PlanCardLicenseDetails) and pass in the label, value, copied state, and copy
handler. Keep the existing behavior with IconButton, withTruncatedText,
copySiteURL, and copyHashed, but render both fields through the new component to
remove duplication and make future copy fields easier to add.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/silver-cars-kneel.md:
- Line 6: The changeset note has a minor grammar issue in the sentence about
where license management should happen. Update the wording in the manage-license
flow description to use “in the subscription page” instead of “in subscription
page” for consistency, keeping the rest of the note unchanged.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx`:
- Line 15: The Callout title in LicenseStatus adds an ellipsis by concatenating
"..." after t('Validating_license'), which bypasses locale control. Update the
LicenseStatus component to move the ellipsis into the translated text itself,
and keep the Callout title bound to the translation result so punctuation can be
handled per locale.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx`:
- Around line 44-52: The license application flow in ManageLicenseModal is
validating the trimmed value but still persisting the raw state in handleApply,
which can save whitespace/newline artifacts instead of the validated license.
Update the apply path to use the same trimmed license value used by
trimmedLicense/debouncedLicense and use that value when calling the save/apply
logic. Make sure ManageLicenseModal and handleApply reference the trimmed
license consistently so the persisted license matches what was validated.
- Around line 70-90: Add a pending/loading guard around the ManageLicenseModal
submission flow so Apply/Remove can’t be clicked repeatedly while the async
request is in flight. Update handleApply and handleRemove to set and clear a
shared loading state around setEnterpriseLicense, then wire that state into
GenericModal via confirmLoading and/or confirmDisabled so the modal blocks
duplicate submissions until the promise resolves.

In `@packages/i18n/src/locales/en.i18n.json`:
- Around line 4534-4535: The `Remove_license_disclaimer` locale string has an
awkward run-on sentence with missing punctuation before “otherwise” and a
missing article before “cloud.” Update that entry in the `en.i18n.json` locale
so the disclaimer reads cleanly and grammatically, while keeping the meaning
unchanged; use the `Remove_license_disclaimer` key to locate and revise the
text.

---

Outside diff comments:
In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx`:
- Line 20: The isSalesAssisted condition in PlanCardTrial is currently
unconditional because of the trailing || true, which prevents the self-service
branch from ever rendering. Update the conditional in PlanCardTrial to use the
intended check on licenseInformation.grantedBy?.method without forcing it true,
so the component can correctly switch between sales-assisted and self-service
behavior.

---

Nitpick comments:
In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx`:
- Line 9: The ManageLicenseModal.spec.tsx test file contains explanatory
implementation comments that should be removed per the coding guidelines. Update
the relevant tests in ManageLicenseModal.spec.tsx to rely on clearer test names,
variable names, and structure instead of inline comments around the license
length/setup and other annotated steps. Keep the intent of those cases in the
spec, but express it through the existing test blocks and symbols like
ManageLicenseModal and its related validation scenarios without adding comment
text.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx`:
- Around line 134-136: The implementation in ManageLicenseModal still contains
an explanatory code comment that violates the no-comments guideline. Remove the
inline comment in the file input change handler inside ManageLicenseModal so the
logic remains unchanged while the implementation code no longer includes a
comment; use the surrounding onChange handler and event.currentTarget.value
reset to locate it.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx`:
- Around line 29-54: The Site URL and Hashed Site URL sections in
PlanCardLicenseDetails repeat the same label/copy-button/value layout, so
extract that shared UI into a small reusable sub-component (for example inside
PlanCardLicenseDetails) and pass in the label, value, copied state, and copy
handler. Keep the existing behavior with IconButton, withTruncatedText,
copySiteURL, and copyHashed, but render both fields through the new component to
remove duplication and make future copy fields easier to add.

In `@apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts`:
- Line 5: The implementation in useValidateLicense contains explanatory comments
that should be removed and replaced with clearer code structure. Update the
logic around the license length check and the type guard so the intent is
obvious from symbol names and control flow, using a well-named constant or
helper in useValidateLicense instead of inline comments. Keep the behavior
unchanged, but eliminate the comments at the referenced spots and make the
surrounding code self-explanatory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8e77ca35-b527-463a-967b-107c3be2bf30

📥 Commits

Reviewing files that changed from the base of the PR and between fcaeed1 and 40be9c4.

📒 Files selected for processing (22)
  • .changeset/silver-cars-kneel.md
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
  • apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx
  • apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx
  • apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • packages/i18n/src/locales/en.i18n.json
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsx
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
🧠 Learnings (10)
📚 Learning: 2026-03-27T14:52:56.865Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 39892
File: apps/meteor/client/views/room/contextualBar/Threads/Thread.tsx:150-155
Timestamp: 2026-03-27T14:52:56.865Z
Learning: In Rocket.Chat, there are two different `ModalBackdrop` components with different prop APIs. During review, confirm the import source: (1) `rocket.chat/fuselage` `ModalBackdrop` uses `ModalBackdropProps` based on `BoxProps` (so it supports `onClick` and other Box/DOM props) and does not have an `onDismiss` prop; (2) `rocket.chat/ui-client` `ModalBackdrop` uses a narrower props interface like `{ children?: ReactNode; onDismiss?: () => void }` and handles Escape keypress and outside mouse-up, and it does not forward arbitrary DOM props such as `onClick`. Flag mismatched props (e.g., `onDismiss` passed to the fuselage component or `onClick` passed to the ui-client component) and ensure the usage matches the correct component being imported.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsx
  • apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsx
  • apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsx
  • apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsx
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx
📚 Learning: 2026-02-10T16:32:42.586Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38528
File: apps/meteor/client/startup/roles.ts:14-14
Timestamp: 2026-02-10T16:32:42.586Z
Learning: In Rocket.Chat's Meteor client code, DDP streams use EJSON and Date fields arrive as Date objects; do not manually construct new Date() in stream handlers (for example, in sdk.stream()). Only REST API responses return plain JSON where dates are strings, so implement explicit conversion there if needed. Apply this guidance to all TypeScript files under apps/meteor/client to ensure consistent date handling in DDP streams and REST responses.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
📚 Learning: 2026-05-11T20:30:35.265Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 40480
File: apps/meteor/client/meteor/startup/accounts.ts:59-61
Timestamp: 2026-05-11T20:30:35.265Z
Learning: In Rocket.Chat’s Meteor client code, when calling `dispatchToastMessage` with `{ type: 'error' }`, pass the raw caught error object as `message` without manual normalization. `dispatchToastMessage` is designed to accept `message: unknown` for error toasts, so avoid converting errors to strings (e.g., `String(error)`) or extracting `error.message` before passing them.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts
  • apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/hooks/useWorkspaceInfo.ts
📚 Learning: 2026-03-16T21:50:37.589Z
Learnt from: amitb0ra
Repo: RocketChat/Rocket.Chat PR: 39676
File: .changeset/migrate-users-register-openapi.md:3-3
Timestamp: 2026-03-16T21:50:37.589Z
Learning: For changes related to OpenAPI migrations in Rocket.Chat/OpenAPI, when removing endpoint types and validators from rocket.chat/rest-typings (e.g., UserRegisterParamsPOST, /v1/users.register) document this as a minor changeset (not breaking) per RocketChat/Rocket.Chat-Open-API#150 Rule 7. Note that the endpoint type is re-exposed via a module augmentation .d.ts in the consuming package (e.g., packages/web-ui-registration/src/users-register.d.ts). In reviews, ensure the changeset clearly states: this is a non-breaking change, the major version should not be bumped, and the changeset reflects a minor version bump. Do not treat this as a breaking change during OpenAPI migrations.

Applied to files:

  • .changeset/silver-cars-kneel.md
📚 Learning: 2026-02-24T19:22:48.358Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 38493
File: apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts:66-67
Timestamp: 2026-02-24T19:22:48.358Z
Learning: In Playwright end-to-end tests (e.g., under apps/meteor/tests/e2e/...), prefer locating elements by translated text (getByText) and ARIA roles (getByRole) over data-qa attributes. If translation values change, update the corresponding test locators accordingly. Never use data-qa locators. This guideline applies to all Playwright e2e test specs in the repository and helps keep tests robust to UI text changes and accessible semantics.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
📚 Learning: 2026-03-06T18:10:15.268Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39397
File: packages/gazzodown/src/code/CodeBlock.spec.tsx:47-68
Timestamp: 2026-03-06T18:10:15.268Z
Learning: In tests (especially those using testing-library/dom/jsdom) for Rocket.Chat components, the HTML <code> element has an implicit ARIA role of 'code'. Therefore, screen.getByRole('code') or screen.findByRole('code') will locate <code> elements even without a role attribute. Do not flag findByRole('code') as invalid in reviews; prefer using the implicit role instead of adding role="code" unless necessary for accessibility.

Applied to files:

  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts
  • apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx
📚 Learning: 2026-04-14T21:10:31.855Z
Learnt from: dougfabris
Repo: RocketChat/Rocket.Chat PR: 36292
File: apps/meteor/client/hooks/useHasValidLocationHash.ts:7-12
Timestamp: 2026-04-14T21:10:31.855Z
Learning: When reviewing files in apps/meteor/client/hooks/, do not treat JSDoc-style comments on React hooks (especially exported hooks) as a violation of any “avoid code comments in implementation” guideline. It’s acceptable to use JSDoc to document the public API of exported hooks (e.g., parameter/return types, intended usage), as long as it documents behavior/contracts rather than adding narrative implementation comments.

Applied to files:

  • apps/meteor/client/hooks/useWorkspaceInfo.ts
🔇 Additional comments (23)
apps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsx (2)

18-32: LGTM!


38-41: 🎯 Functional Correctness

No placeholder issue here. The translation already includes <a>subscription page</a>, so Trans will render the redirect link as clickable.

			> Likely an incorrect or invalid review comment.
apps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsx (1)

6-6: LGTM!

Also applies to: 34-37

apps/meteor/client/hooks/useWorkspaceInfo.ts (1)

4-74: LGTM!

apps/meteor/client/views/admin/subscription/hooks/useValidateLicense.ts (1)

14-36: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.ts (1)

1-28: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.ts (1)

1-106: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsx (1)

1-27: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx (2)

1-43: LGTM!

Also applies to: 92-117, 121-181


143-143: 🔒 Security & Privacy

Confirm withRichContent is safe on this input. It appears to be a rich-text/display styling prop, not an input rendering feature, but the TextAreaInput prop surface here is unclear.

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsx (1)

1-8: LGTM!

Also applies to: 23-89, 91-123

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.ts (1)

1-37: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.ts (1)

1-108: LGTM!

apps/meteor/client/views/admin/subscription/SubscriptionPage.tsx (1)

145-145: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsx (1)

66-66: LGTM!

packages/i18n/src/locales/en.i18n.json (1)

490-490: LGTM!

Also applies to: 654-655, 798-798, 1929-1929, 2226-2226, 2796-2796, 3196-3204, 3402-3403, 4077-4077, 4528-4533, 5671-5671, 5844-5847, 6029-6029, 7086-7086

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.ts (1)

1-1: LGTM!

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsx (2)

1-13: LGTM!

Also applies to: 55-73


14-15: 🎯 Functional Correctness

No type mismatch hereuseSetting(..., '') narrows both siteURL and enterpriseLicense to string, so the ManageLicenseModal prop contract is satisfied.

			> Likely an incorrect or invalid review comment.
apps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsx (1)

3-3: LGTM!

Also applies to: 12-26

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsx (1)

5-5: LGTM!

Also applies to: 20-20

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsx (1)

7-7: LGTM!

Also applies to: 62-62

apps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx (1)

8-8: LGTM!

Also applies to: 54-54

Comment thread .changeset/silver-cars-kneel.md

if (isValidating) {
return (
<Callout icon='reload' type='info' title={`${t('Validating_license')}...`}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Move the ellipsis into the translation string.

Concatenating '...' after t('Validating_license') bypasses i18n control over punctuation placement/glyph for other locales.

🌐 Proposed fix
-			<Callout icon='reload' type='info' title={`${t('Validating_license')}...`}>
+			<Callout icon='reload' type='info' title={t('Validating_license_ellipsis')}>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsx`
at line 15, The Callout title in LicenseStatus adds an ellipsis by concatenating
"..." after t('Validating_license'), which bypasses locale control. Update the
LicenseStatus component to move the ellipsis into the translated text itself,
and keep the Callout title bound to the translation result so punctuation can be
handled per locale.

Comment on lines +44 to +52
const trimmedLicense = license.trim();
const debouncedLicense = useDebouncedValue(trimmedLicense, 500);
const { data: validation, isPending, isError } = useValidateLicense(debouncedLicense);

const isEmpty = trimmedLicense === '';
const isPlausible = isPlausibleLicense(trimmedLicense);
const isValidating = isPlausible && (trimmedLicense !== debouncedLicense || isPending);

const isCurrentLicense = !isEmpty && trimmedLicense === enterpriseLicense.trim();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply the validated (trimmed) license, not the raw state.

Validation runs against trimmedLicense/debouncedLicense, but handleApply persists the raw license state (Line 72). A license read from a .txt file or pasted text commonly carries a trailing newline/whitespace that was stripped before validation but not before being saved — so a "valid" preview can still persist an untrimmed value that wasn't actually the one validated.

🐛 Proposed fix
 	const handleApply = async () => {
 		try {
-			await setEnterpriseLicense(license);
+			await setEnterpriseLicense(trimmedLicense);
 			invalidateLicense(100);

Also applies to: 70-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx`
around lines 44 - 52, The license application flow in ManageLicenseModal is
validating the trimmed value but still persisting the raw state in handleApply,
which can save whitespace/newline artifacts instead of the validated license.
Update the apply path to use the same trimmed license value used by
trimmedLicense/debouncedLicense and use that value when calling the save/apply
logic. Make sure ManageLicenseModal and handleApply reference the trimmed
license consistently so the persisted license matches what was validated.

Comment on lines +70 to +90
const handleApply = async () => {
try {
await setEnterpriseLicense(license);
invalidateLicense(100);
onCancel();
dispatchToastMessage({ type: 'success', message: t('Cloud_License_applied_successfully') });
} catch (err) {
dispatchToastMessage({ type: 'error', message: err });
}
};

const handleRemove = async () => {
try {
await setEnterpriseLicense('');
invalidateLicense(100);
onCancel();
dispatchToastMessage({ type: 'success', message: t('License_removed_successfully') });
} catch (err) {
dispatchToastMessage({ type: 'error', message: err });
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

No loading guard during apply/remove submission.

handleApply/handleRemove don't set a pending/loading state, and confirmDisabled/no confirmLoading is passed to GenericModal. A user can click Apply/Remove multiple times before the awaited call resolves, triggering duplicate setting dispatches, toasts, and invalidations.

🔒 Proposed fix
+	const [isSubmitting, setIsSubmitting] = useState(false);
+
 	const handleApply = async () => {
+		setIsSubmitting(true);
 		try {
 			await setEnterpriseLicense(trimmedLicense);
 			invalidateLicense(100);
 			onCancel();
 			dispatchToastMessage({ type: 'success', message: t('Cloud_License_applied_successfully') });
 		} catch (err) {
 			dispatchToastMessage({ type: 'error', message: err });
+		} finally {
+			setIsSubmitting(false);
 		}
 	};
...
 			confirmDisabled={!isLicenseValid || isCurrentLicense || isValidating}
+			confirmLoading={isSubmitting}

Also applies to: 118-120

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsx`
around lines 70 - 90, Add a pending/loading guard around the ManageLicenseModal
submission flow so Apply/Remove can’t be clicked repeatedly while the async
request is in flight. Update handleApply and handleRemove to set and clear a
shared loading state around setEnterpriseLicense, then wire that state into
GenericModal via confirmLoading and/or confirmDisabled so the modal blocks
duplicate submissions until the promise resolves.

Comment on lines +4534 to +4535
"Remove_license_confirmation": "Removing the license key will result in automatic application of Rocket.Chat community license. Community license restrictions will take effect immediately.",
"Remove_license_disclaimer": "If a license is removed the workspace must be restarted to take effect. If the workspace is connected to the cloud the license should be canceled there first otherwise cloud will provide the license to the workspace again during the restart.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Awkward run-on sentence in Remove_license_disclaimer.

Missing punctuation before "otherwise" and a missing article before "cloud" make this hard to parse.

✏️ Proposed fix
-  "Remove_license_disclaimer": "If a license is removed the workspace must be restarted to take effect. If the workspace is connected to the cloud the license should be canceled there first otherwise cloud will provide the license to the workspace again during the restart.",
+  "Remove_license_disclaimer": "If a license is removed, the workspace must be restarted to take effect. If the workspace is connected to the cloud, the license should be canceled there first, otherwise the cloud will provide the license to the workspace again during the restart.",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Remove_license_confirmation": "Removing the license key will result in automatic application of Rocket.Chat community license. Community license restrictions will take effect immediately.",
"Remove_license_disclaimer": "If a license is removed the workspace must be restarted to take effect. If the workspace is connected to the cloud the license should be canceled there first otherwise cloud will provide the license to the workspace again during the restart.",
"Remove_license_confirmation": "Removing the license key will result in automatic application of Rocket.Chat community license. Community license restrictions will take effect immediately.",
"Remove_license_disclaimer": "If a license is removed, the workspace must be restarted to take effect. If the workspace is connected to the cloud, the license should be canceled there first, otherwise the cloud will provide the license to the workspace again during the restart.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/src/locales/en.i18n.json` around lines 4534 - 4535, The
`Remove_license_disclaimer` locale string has an awkward run-on sentence with
missing punctuation before “otherwise” and a missing article before “cloud.”
Update that entry in the `en.i18n.json` locale so the disclaimer reads cleanly
and grammatically, while keeping the meaning unchanged; use the
`Remove_license_disclaimer` key to locate and revise the text.

@dougfabris
dougfabris changed the base branch from worktree-majestic-wibbling-jellyfish to develop July 6, 2026 13:54
@dougfabris
dougfabris requested review from a team as code owners July 6, 2026 13:54
@dougfabris
dougfabris changed the base branch from develop to worktree-majestic-wibbling-jellyfish July 6, 2026 18:58
@dougfabris
dougfabris removed request for a team July 6, 2026 18:59
@ggazzo
ggazzo changed the base branch from worktree-majestic-wibbling-jellyfish to develop July 6, 2026 19:36
@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Jul 6, 2026
@dionisio-bot dionisio-bot Bot added the stat: ready to merge PR tested and approved waiting for merge label Jul 6, 2026
@ggazzo
ggazzo merged commit b2b5edf into develop Jul 6, 2026
82 of 85 checks passed
@ggazzo
ggazzo deleted the feat/improve-manual-license branch July 6, 2026 19:40
This was referenced Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants