1569 Commits
Author SHA1 Message Date
1dd851498a feat(admin): add documentation helper link in HeaderLayout (revival of #23328) (#26422)
* feat(admin): add documentation helper link in HeaderLayout

Revival of #23328 (by @Mcastres), rebased on develop with review feedback
addressed and additional improvements:

- Replace hand-rolled find with react-router matchRoutes for proper path
  matching and specificity ranking
- Update doc URLs to canonical docs.strapi.io paths (drop redirected
  /user-docs/* and /dev-docs/* entries)
- Restore section anchors that were lost during doc-site reorganization
  (#providers, #email-templates, #advanced-settings, profile, rbac)
- Align array path entries with actual registered admin routes (rename
  /plugins/content-type-builder, /plugins/content-releases, settings
  prefix for purchase-content-releases and list-plugins, drop dead
  /marketplace external link)
- Add opt-out flag admin.flags.docLinks for white-label deployments
  (requested by @derrickmehaffy)
- Memoize getMatchingDocLink result per pathname
- Place doc link button next to primary action via gap on parent Flex
  (per @remidej review)
- Drop async from getMatchingDocLink, derive docLink synchronously from
  pathname (per @remidej review)

Closes #23328

Co-authored-by: Maxime Castres <17828745+Mcastres@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(admin): move DocLink type to getMatchingDocLink module

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

* fix(admin): add docLinks flag to EE Window strapi type

The EE admin custom.d.ts redeclares window.strapi.flags and was missing
the docLinks property, causing test:ts:front to fail with TS2339 in
HeaderLayout.tsx.

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

* test: add getMachingDocLink tests

---------

Co-authored-by: Maxime Castres <17828745+Mcastres@users.noreply.github.com>
Co-authored-by: Ben Irvin <ben@innerdvations.com>
2026-05-26 09:59:00 +02:00
Andrei L b5452ab89f chore(eslint): migrate .eslintrc + .eslintignore to .eslintrc.cjs (#26216)
* chore(eslint): migrate .eslintrc + .eslintignore to .eslintrc.cjs

Consolidates the per-package ESLint setup into a single typed
`.eslintrc.cjs` file across the monorepo (177 files touched).

Why
- `.eslintignore` is removed in ESLint v9/v10. Folding ignore patterns
  into `ignorePatterns` inside the rc file is the supported migration
  path and unblocks future ESLint upgrades (which will likely be needed
  alongside the in-flight TypeScript 6 migration).
- `.cjs` extension is explicit about module format, so the configs keep
  working if any package later adopts `"type": "module"` (no CJS/ESM
  surprises).
- Wrapping each config in `// @ts-check` + a `Linter.Config`-typed
  `const config` block gives editor autocomplete and catches typos.
  Surfaced two real duplicates in
  `eslint-config-custom/back/typescript.js`
  (`node/no-unsupported-features/es-syntax`,
  `node/no-missing-import`), now deduplicated.
- `extends` references switched from the `custom/*` shorthand to the
  explicit `eslint-config-custom/*` package name. ESLint resolves both,
  but the explicit form makes the source unambiguous.
- Added `coverage/` to `ignorePatterns` so coverage output produced
  inside a workspace is not linted.

Misc
- `examples/kitchensink` had ESLint configured but no `lint` script;
  added one so the config actually runs.
- `nx.json` hash inputs updated:
  - removed `.eslintignore` (no longer exists)
  - replaced `{projectRoot}/.eslintrc.js` with
    `{projectRoot}/**/.eslintrc.cjs` so nested package configs are also
    part of the cache key (the previous glob missed them).
- `packages/utils/upgrade/tsconfig.eslint.json` include path updated
  from `.eslintrc.js` to `.eslintrc.cjs`.

Verification
- `eslint --print-config` resolves cleanly on sampled back/front
  packages (admin, users-permissions, upload).
- Full `pnpm -r lint` is clean.

* chore(eslint): set tsconfigRootDir for vscode-eslint typed linting

CLI lint resolved tsconfig paths via cwd, but vscode-eslint runs from
the workspace root and could not locate per-package tsconfigs. Set
parserOptions.tsconfigRootDir to __dirname in each package's
.eslintrc.cjs so the editor resolves typed-lint configs correctly.

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

* chore(eslint): self-ignore configs, scope coverage to jest packages

- Replace `env: { es6: true }` workaround with `.eslintrc.cjs` in
  ignorePatterns so eslint never lints the config file itself
  (root cause of the "const is not a keyword" parse error in es5)
- Add `.eslintrc.cjs` to ignorePatterns across all configs that
  were missing it
- Restrict `coverage/` ignorePatterns to workspace roots that
  actually run jest; drop from non-test packages and admin/server
  subdir configs (tests run from the workspace root)
2026-05-12 07:32:12 +00:00
6d83054647 chore(deps): migrate msw 1.x → 2.13.4 across admin test suites (#26065)
* chore(deps): bump msw to 1.3.5 and typedoc to 0.25.13

Widens TypeScript peer ranges in msw (>= 4.4.x) and typedoc (adds 5.4.x),
unblocking a future bump to typescript 5.6 required by react-intl 7.

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

* chore(deps): bump msw to 2.13.4 across admin test suites

Migrates 8 workspaces (admin, content-manager, content-releases, upload,
review-workflows, plugin-i18n, plugin-documentation, plugin-users-permissions)
from msw 1.3.5 to 2.13.4; bumps email's unused dep for consistency.

Handler changes:
- rest.X((req, res, ctx) => res(ctx.json(X))) → http.X(() => HttpResponse.json(X))
- res(ctx.status(N)) → new HttpResponse(null, { status: N })
- res.once(...) → http.X(path, handler, { once: true })
- res.networkError(...) → HttpResponse.error()
- await req.json<T>() / casts → http.METHOD<Params, Body>(...) generics
- RequestHandler → HttpHandler
- Empty 200 responses use HttpResponse.json({}) to avoid cross-realm
  SyntaxError instanceof check in getFetchClient.ts

Jest setup (per msw frequent-issues guide):
- jest-preset.front.js: customExportConditions: [''] so msw/node resolves
  under jsdom; .mjs swc transform; ESM allow-list for msw deps
  (rettime, @open-draft, outvariant, until-async, etc.)
- packages/admin-test-utils/src/environment.ts: polyfill Node fetch +
  streams + BroadcastChannel + URL into jsdom global. Blob/File/FormData
  intentionally left as jsdom's — Strapi app code threads them through
  jsdom-only APIs (FileReader, Image, HTMLFormElement) where realm matters.
  Comment in env.ts documents the trade-off.

Verified vs develop+msw@1.3.5 baseline: 0 net regressions across all 7
workspaces with msw test files; TypeScript clean.

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

* Revert "chore(deps): bump typedoc to 0.25.13"

The typedoc bump was bundled with the msw 1.3.5 bump in 39d169cfbb but
isn't required for the msw v2 migration. Reverting the docs/ and
core/types typedoc dep back to 0.25.10 to keep this branch focused on
the msw upgrade.

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

* chore(tests): type msw handler params via generics

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

* chore(tests): address review feedback on msw v2 migration

- env.ts: snapshot jsdom globals in setup(), restore in teardown() so
  per-test mutations cannot leak across files in a Jest worker.
- env.ts: collapse polyfill list and value map into a single NODE_GLOBALS
  source of truth; document the fetch trade-off (Node undici vs jsdom).
- handlers: replace `<never, Body>` generics with `<Record<string, never>,
  Body>` (msw v2 convention; `{}` blocked by ban-types).
- email: drop unused msw devDep entirely (zero source usage in workspace).

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

* chore(tests): use realm-agnostic SyntaxError check; drop URL polyfill

Two follow-ups to the msw v2 migration:

- `getFetchClient.responseInterceptor` now matches `error?.name === 'SyntaxError'`
  rather than `instanceof SyntaxError`. Constructor identity differs across JS realms
  (under jsdom + Node fetch, an empty 200 throws a Node-realm `SyntaxError` that
  fails the jsdom-realm `instanceof` check; the same is true in browsers when the
  Response originates from a service worker / iframe). Name comparison is realm-
  agnostic. With this in place, empty 200 handlers can return `new HttpResponse()`
  again (closer to v1's `res(ctx.status(200))`) instead of the `HttpResponse.json({})`
  workaround.

- Drop `URL` / `URLSearchParams` from the env.ts polyfill list. jsdom already
  provides browser-spec versions, and Node's `URL.createObjectURL` only accepts
  Node-realm `Blob` — replacing it broke the "from computer" upload path in
  `LogoInput.tsx`. MSW v2's interceptor doesn't require these to come from Node.

env.ts comments restructured: all polyfill rationale lives in one top-of-file block
so the body is short and the trade-offs are documented in one place.

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

* chore(tests): switch content-type-builder jest env to shared polyfilled env

`aiClient.test.ts` constructs `new Response(...)` which jsdom does not provide
spec-compliantly. Align CTB with the other admin test suites by using
`@strapi/admin-test-utils/environment`, which polyfills Node's
`fetch`/`Request`/`Response`/streams into the jsdom global.

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

* fix(tests): make msw v2 + undici body + jsdom realms work together

Three regressions surfaced by running the full front test suite under
jsdom + Node's undici fetch polyfill:

1. `TypeError: Body is unusable: Body has already been read` in every
   handler doing `await request.json()`. MSW v2's `RequestHandler.run()`
   calls `request.clone()` before invoking the resolver (to keep a copy
   for post-hoc logging). Under Node's undici, `Request.clone()` tees the
   underlying `ReadableStream` and locks the original body, so the
   resolver's `.json()` throws. Fix in `environment.ts`: wrap the global
   `Request` constructor to stash the stringified body on every instance
   and patch `Request.prototype.json` / `.text` to return the stash when
   present. Handlers now get the body regardless of how many times msw
   clones internally.

2. `expect.any(Array)` and `toStrictEqual` failing with "serializes to
   the same string" against msw response bodies. `Response.prototype.json`
   runs Node's `JSON.parse`, so the resulting objects carry Node-realm
   `Object`/`Array` prototypes; jsdom's `instanceof`/strict-equality
   matchers reject them. Patch `Response.prototype.json` to parse with
   the jsdom-realm `JSON`.

3. `useWidgets > should handle network errors gracefully` expected the
   toast text "Network error", which msw v1's `res.networkError('Network
   error')` surfaced verbatim. v2's `HttpResponse.error()` drops the
   custom message and fetch throws `TypeError: Failed to fetch`; update
   the assertion accordingly.

Also: `UIDInput` uses `jest.useFakeTimers()`. Adding `doNotFake:
['queueMicrotask', 'setImmediate']` keeps per-test runtime at ~1s
instead of ~10s (plain fake timers stall undici's internal microtasks
between request emission and handler resolution), and an `afterEach`
restores real timers so a failing test cannot leak fake timers into
the next one.

Verified with `nx run-many --target=test:front --parallel=1` across all
12 projects — all suites green.

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

* test(admin): assert toast container in Widgets error tests

Replace text-match (`findByText('Server error' | 'Failed to fetch')`) with
`[data-sonner-toast]` container assertion. Decouples the test from toast
copy and from msw v1/v2 network-error message differences.

Addresses @jhoward1994's review comment on PR #26065.

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

* test(admin): assert toast text, scope msw handlers, use jsdom env

- Widgets tests: match actual toast text (Server error / fetch failure
  regex) instead of data-sonner-toast container.
- ConfirmBulkActionDialog: mark msw runtime handlers { once: true } so
  they do not leak across cases.
- content-type-builder: jest testEnvironment back to jsdom.

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

* chore(tests): split jest env patches into modules

Refactor packages/admin-test-utils/src/environment.ts by extracting
the msw v2 + undici body + jsdom realm shims into dedicated modules
under src/patches/ (node-globals, request-body-stash,
response-json-realm, types).

Co-Authored-By: Jamie Howard <jhoward1994@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:45:04 +00:00
Nico André bc0b0e9449 feat(core/*): introduce strapi.ai namespace (#25886) 2026-04-17 08:46:33 +02:00
Yohei Yamamoto 12dfbe97c9 chore: fix typos in comments and docs 2026-04-07 14:32:50 +02:00
Jamie Howard 1095610195 fix(history): improve error handling and batch deletion in cron jobs (#25425)
* fix(history): improve error handling and batch deletion in cron jobs

* chore(content-manager): fix error handling
2026-02-16 11:03:04 +00:00
Adrien L 3615e59020 fix: responsive cm header (#25203) 2026-01-21 13:55:26 +01:00
Marco A. e343fe10e7 chore: remove all occurances of aiLicenseKey (#24908) 2026-01-16 13:14:45 +02:00
Adrien L 90d110d128 fix: optimize images (#25047) 2025-12-22 10:01:52 +01:00
markkaylor 4310e958c5 chore(admin): isolate ai apis to ee folder (#24834) 2025-11-20 11:01:48 +01:00
Mark Kaylor 884a932aa1 Merge branch 'main' of github.com:strapi/strapi into develop 2025-11-20 09:54:30 +01:00
markkaylor 068169c1ac fix(admin): ai disabled by default for growth license (#24868) 2025-11-19 10:27:07 +01:00
HichamELBSI 18948ab6b4 chore: update DS version to stable 2025-11-19 10:20:19 +01:00
markkaylor ef9fb10a18 fix(admin): ai admin config can be set to true for non growth license (#24835) 2025-11-13 11:39:49 +01:00
Ocneanu Vlad 70f91ede64 fix: optional chaining in getRetentionDays function (#24830) 2025-11-13 12:14:58 +02:00
markkaylor cababa5cb1 chore: update ai tracking events (#24758) 2025-11-03 15:13:11 +01:00
HichamELBSI 93242b3945 fix: navigate reset on query param change 2025-10-30 10:24:41 +01:00
Jamie Howard 91f3ae970f enhancement: cookie security configuration (#24524) 2025-10-14 09:15:09 +02:00
+3 5e751dbf11 feat: ai generated image metadata (#24422)
* feat: chat

feat: apply changes
feat: integrate with ctb

feat: marker

chore: remove comment

feat: new chat

feat: copy message

feat: upload modal

feat: upload file

feat: errors and stop chat

chore: refactor transforms
chore: format relations

chore: chat title

chore: remove architect dependency

feat: empt state

chore: improve text area focus

* feat: chat imports

feat: resizable text area
fix: re add chat
chore: translations
feat: env vars

fix: minor chat issues

feat: feedback

fix: rebase

feat: import folder

feat: limits

feat: attachments dropzone

chore: file attachments cleanup
chore: track chat id

feat: figma import

fix :token

feat: figma token
fix: attribute status when chat makes updates

feat: image upload

* feat: staging integration

* chore: remove logs

* feat: use tool call result instead of annotation

* fix: invalid component uid

* chore: chat input focus ring

* fix: ui issues

* fix: default draft and publish and do not modify singular name

* fix: minor transforms

* fix: linting

* test(front): update snapshots

* chore: fix misplaced getstarted project schema

* chore: remove unused import

* security: validate exact path of host

* fix: define process better for playwright to work

* fix: process env in vite config for playwright

* chore: use production url

* feat(ctb): Tracking events for AI Chat interaction (#23731)

* feat(content-type-builder): WIP tracking events for chat interactions

* fix: typescript build errors

* fix: event name and build errors

* chore: send ai key with analytics

* chore: actually send licenseKey

* chore: send ailicensekey with groupproperties

* fix: didStartNewChat

* chore: track new chats

* feat: enhance attachment type management in AI chat components

* fix: update chat status handling in ChatProvider component

* feat: add optional aiLicenseKey to Strapi interface

---------

Co-authored-by: Jamie Howard <jhoward1994@gmail.com>

* refactor: remove didusersendmessage tracking event (#23777)

* fix: merge conflict

* fix: send projectId to AI server

* feat: add a enabled config for AI features (#24060)

* feat: add getAiToken endpoint (#24172)

* feat: add getAiToken route

* fix: change route name, remove project id

* fix: type issue and fix schema

* feat: retrieve ai token from frontend (#24226)

---------

Co-authored-by: Jamie Howard <jhoward1994@gmail.com>

* fix: use primary500 for links in ai chat

* chore: migrate to AI SDK v5 (#24252)

* fix: migrate code for v5

* t:wq

* feat: push schemas to ctb

* chore: remove old code

* chore: remove ts-no-check

* chore: fix comment

* fix: ai server logs (#24318)

* test(back): fix error log tests

---------

Co-authored-by: Marc Roig <marc12info@gmail.com>

* fix: configure ai ctb csp middleware without overriding user or default config

* future(upload): generate image metadata on file upload (#24365)

* chore: create aiMetadata service with isEnabled

* chore: extract getAiToken to service

* fix: unit test

* future(upload): generate metadata with ai

* fix: ts build

* fix: only send images to ai server

* test: add unit tests

* fix: unit test

---------

Co-authored-by: markkaylor <mark.kaylor@strapi.io>

* AI media lib bulk update (#24414)

* feat(packages): adding endpoint for bulk update

* feat(packages): linting

* feat(packages): adding tests

* feat(packages): cleanup

* feat: guided tour for ai ctb (#24411)

* feat(upload): adding aiMetadata into settings (#24468)

feat(upload): adding aiMetadata into settings

* feat: add AI upload modal (#24407)

* chore: create aiMetadata service with isEnabled

* chore: extract getAiToken to service

* fix: unit test

* future(upload): generate metadata with ai

* feat: add AI upload modal

* feat: add edit and delete to upload modal

* fix: remove sparkle icon on edit

* fix: add error handling

* chore: refactor ai upload modal reducer

* fix: catch ai token generation error

* chore: add useBulkEdit hook

* feat: connect to bulk edit endpoint

* fix: e2e test

* fix: ci in both ce and ee

---------

Co-authored-by: Rémi de Juvigny <remi.dejuvigny@strapi.io>
Co-authored-by: Rémi de Juvigny <8087692+remidej@users.noreply.github.com>

* feat(upload): applying ai enabled logic for media library (#24486)

feat(upload): applying ai enabled logic for media library

* fix: sparkle icon

* fix: set ai server prod url

* fix: default config to enabled

* fix: cursor moving to the end when editing text

* fix: upload in the right folder

* fix: close modal when deleting last item

* chore: use STRAPI_AI_URL everywhere

* fix: bulk upload from frontend

* fix: restore sparkle icon on inputs

* fix: unit test ci

* feat(upload): generating metadata from thumbnail (#24515)

* feat(upload): generating metadata from thumbnail

* feat(upload): fixing linting issue

* feat(upload): fixing tests and addressiing feedback

* feat(upload): fixing lint

* fix: check for cms-ai entitlement

* fix: tests

* fix: race condition failing front unit tests

---------

Co-authored-by: Marc-Roig <marc12info@gmail.com>
Co-authored-by: Ben Irvin <ben@innerdvations.com>
Co-authored-by: Bassel Kanso <bassel.kanso@strapi.io>
Co-authored-by: Ben Irvin <ben.irvin@strapi.io>
Co-authored-by: Jamie Howard <jhoward1994@gmail.com>
Co-authored-by: Jamie Howard <48524071+jhoward1994@users.noreply.github.com>
Co-authored-by: Bassel Kanso <basselkanso82@gmail.com>
Co-authored-by: Ziyi <daydreamnation@live.com>
Co-authored-by: markkaylor <mark.kaylor@strapi.io>
Co-authored-by: Araksya Gevorgyan <31159659+araksyagevorgyan@users.noreply.github.com>
Co-authored-by: Adrien L <thewebsdoor@gmail.com>
2025-10-07 06:14:34 -04:00
Bassel Kanso e326c69a49 Merge branch 'main' into develop 2025-10-06 18:03:32 +03:00
Bassel Kanso 1f56e93196 enhancement: AI notification system improvements (#24505)
* enhancement: AI notification system improvements

* enhancement: increase notification time

* test: add timeout value
2025-10-06 16:44:14 +03:00
Adrien L 59b1b35ce0 feat: responsive navigation (#24455) 2025-10-06 15:42:23 +02:00
Ben Irvin 8b11c0eca0 feat(ai): AI Chat for Schema Creation (#23730) 2025-10-02 13:13:47 +02:00
Jamie Howard d23f1485e5 feat: Advanced Session Configuration (#24346) 2025-09-23 12:04:29 +02:00
Adrien L 330c50cc41 fix: update tracking on homepage widgets (#24153) 2025-08-13 11:32:10 +02:00
Adrien L d9637048d9 feat: last activity homepage widget (#24068) 2025-08-06 15:42:00 +02:00
DMehaffy 20c174685c fix: allow audit logs to be increased up to the license maximum (#24061) 2025-07-31 07:58:50 -07:00
Giulio Montagner e648c19c20 chore: replace node-schedule with strapi.cron service 2025-07-28 14:58:05 +02:00
akash-dabhi-qedandAdrien Lepoutre 388e513cc3 fix: resolved filter overlap with datepicker by reducing zIndex of filter … (#23884)
Co-authored-by: Adrien Lepoutre <thewebsdoor@gmail.com>
2025-07-23 14:28:42 +02:00
mazzucchelli ef8b017af0 chore: update settings sidebar links ui 2025-07-18 10:52:30 +02:00
DMehaffy e3eb76a86a fix: remove policy from license-limit-info that breaks releases (#23424) 2025-06-25 12:22:53 -04:00
Arthur Moreau 4ac7f54e67 feat(cli): Introduce the Growth Trial prompt (#23498) 2025-06-03 19:58:02 +02:00
Bassel Kanso b288881356 test(admin): add tests for reducer auth actions and Login SSO (#23369) 2025-04-16 11:26:48 +03:00
Bassel Kanso 0c37f5c278 fix: sso login jwt formatting issues (#23247) 2025-03-28 12:54:14 +01:00
Bassel Kanso d0c6971f9e fix: preserve cookie in sso login (#23240) 2025-03-26 11:48:35 +01:00
Simon Norris 16829452be fix: prettier notification tests 2025-03-24 21:04:40 +01:00
Simon Norris f4754bded9 fix: updating license notifications and their tests 2025-03-24 20:56:41 +01:00
Simon Norris 31611b4598 fix: prettier on my notification file 2025-03-24 19:08:14 +01:00
Simon Norris f89dce5534 Merge branch 'develop' into feature/1103-manage-seats 2025-03-24 18:50:51 +01:00
Simon Norris 4be9ba6828 fix: handling type === gold check in license notifications 2025-03-24 18:48:14 +01:00
Bassel Kanso 3aeb285c7b enhancement: admin session presists across tabs (#23188)
* enhancement: admin session presists across tabs

* test: clear cookies in login tests

* test: remove tests from EE
2025-03-24 16:10:25 +02:00
Simon Norris 7d410c85ab fix: now my code is prettier 2025-03-24 12:32:28 +01:00
Simon Norris c3f2ca2808 fix: updating tests for Managing seats 2025-03-24 12:16:33 +01:00
Simon Norris c7e57b2c40 fix: removing type because we use the licenseLimitInformation instead 2025-03-24 10:25:11 +01:00
Simon Norris 01abcb251f fix: updating message for Gold licenses 2025-03-24 10:25:11 +01:00
HichamELBSI 1cf3c821d1 fix: manage seats link for gold license 2025-03-24 10:25:11 +01:00
Simon Norris ff5b6069b9 fix: reverting licenseType to type 2025-03-24 10:25:11 +01:00
Simon Norris ea6268bc6a fix: adding license type with it's typing 2025-03-24 10:25:11 +01:00
Simon Norris 4ac8513411 fix: one more Strapi Cloud condition for Manage seats 2025-03-24 10:24:56 +01:00
Simon Norris ccc540ffcb feat: adding a link to chargebee portal for managing seats 2025-03-19 15:29:26 +01:00