feat: Improve manual license management - #40916
Conversation
…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>
|
Looks like this PR is ready to merge! 🎉 |
🦋 Changeset detectedLatest commit: 40be9c4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
WalkthroughThis 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. ChangesLicense Management Feature
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
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')
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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. Comment |
1acd309 to
caf79ce
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
caf79ce to
4ca7893
Compare
4ca7893 to
7920406
Compare
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>
c4bdea3 to
f680c49
Compare
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>
f680c49 to
a0c6663
Compare
9b49e40 to
40be9c4
Compare
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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
|| truemakes the sales-assisted branch unconditional. Inapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsx:20,licenseInformation.grantedBy?.method !== 'self-service' || truealways evaluates totrue, so the self-service message and button path can never render. Remove the|| trueor 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 valueComments 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 valueComment 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 valueComments 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 winDuplicate "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
📒 Files selected for processing (22)
.changeset/silver-cars-kneel.mdapps/meteor/client/hooks/useWorkspaceInfo.tsapps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsxapps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsxapps/meteor/client/views/admin/subscription/SubscriptionPage.tsxapps/meteor/client/views/admin/subscription/components/cards/FeaturesCard.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsxapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tspackages/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.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.tsapps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsxapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/meteor/client/views/admin/subscription/SubscriptionPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsxapps/meteor/client/hooks/useWorkspaceInfo.tsapps/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.tsextension for test files (e.g.,login.spec.ts)
Files:
apps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/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.tsxapps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsxapps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsxapps/meteor/client/views/admin/subscription/SubscriptionPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsxapps/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.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.tsapps/meteor/client/views/admin/settings/SettingsGroupSelector/SettingsGroupSelector.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardCommunity.tsxapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/settings/groups/EnterpriseGroupPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardPremium.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseStatus.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardLicenseDetails.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/LicenseFilePreview.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/meteor/client/views/admin/subscription/SubscriptionPage.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/PlanCardTrial.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.tsxapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/ManageLicenseModal.spec.tsxapps/meteor/client/hooks/useWorkspaceInfo.tsapps/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.tsapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/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.tsapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/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.tsapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/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.tsapps/meteor/client/views/admin/subscription/hooks/useValidateLicense.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/getLicenseInvalidMessage.spec.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/index.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/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.tsapps/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.tsapps/meteor/client/views/admin/subscription/components/cards/PlanCard/ManageLicenseModal/useLicenseFileInput.spec.tsapps/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 CorrectnessNo placeholder issue here. The translation already includes
<a>subscription page</a>, soTranswill 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 & PrivacyConfirm
withRichContentis safe on this input. It appears to be a rich-text/display styling prop, not an input rendering feature, but theTextAreaInputprop 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 CorrectnessNo type mismatch here —
useSetting(..., '')narrows bothsiteURLandenterpriseLicensetostring, so theManageLicenseModalprop 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
|
|
||
| if (isValidating) { | ||
| return ( | ||
| <Callout icon='reload' type='info' title={`${t('Validating_license')}...`}> |
There was a problem hiding this comment.
📐 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.
| 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(); |
There was a problem hiding this comment.
🎯 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.
| 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 }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🩺 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.
| "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.", |
There was a problem hiding this comment.
📐 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.
| "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.
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:
How to test
Issue(s)
Steps to test or reproduce
Further comments
CORE-2104
Summary by CodeRabbit
New Features
Bug Fixes