Repository navigation
Conversation
Implement the Billing frontend consuming the backend Billing API (subscriptions, token quota and Stripe payments): - data/: BillingApiService (subscription / usage / upgrade / cancel / reactivate), BillingStore signal store, response/request models and a display plan catalog (limits + pricing) kept in sync with the backend PlanCatalog. - Settings -> Billing page: current plan with status/renewal, a plan grid (Free/Pro/ Enterprise) with upgrade, and cancel/reactivate. Immediate activation with the fake gateway; redirect to Stripe hosted checkout when the response requires it. - Settings -> Usage page: AI token consumption vs. the plan allowance for the current period, with an amber/red progress bar. - Checkout return landings (/billing/success, /billing/cancel) for the Stripe flow. - Wire the routes, enable the previously "soon" Billing/Usage nav items, and add billing/usage i18n plus billing error-code messages (en + es).
Adds SessionParticipant and SessionPresenceMessage (PRESENCE_STATE) to the session realtime union, mirroring the backend contract: a full roster snapshot with a distinct participant count carried on the existing per-session topic.
Handles PRESENCE_STATE on the existing per-session subscription and exposes activeParticipants/activeParticipantCount for the live session only (historical sessions show no roster). Presence is cleared on reset.
An overlapping avatar stack with a live pulse, a "+N" overflow bubble and a viewer count, reusing the shared Avatar (image + monogram fallback). New joiners animate in and names show as tooltips. Adds the discovery.presence i18n keys (en/es).
Renders the active-participants roster in the discovery-chat header and inside the live session bar, bound to the store's activeParticipants. It appears only while a session is live and someone is present.
Verifies PRESENCE_STATE populates the roster for the live session, presence for a non-live session is not surfaced, and reset clears it.
apiUrl was never actually read anywhere (HTTP calls use hardcoded relative /api paths), but wsUrl is real and pointed at a domain that doesn't exist. Both empty now: HTTP stays relative, and the WebSocket client falls back to the current origin. CloudFront (reqsai-infra) proxies /api/* and /ws/* to the backend under the same origin, so no absolute URL is needed.
Bumps nginx from 1.27-alpine to 1.31-alpine. --- updated-dependencies: - dependency-name: nginx dependency-version: 1.31-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps node from 24-alpine to 26-alpine. --- updated-dependencies: - dependency-name: node dependency-version: 26-alpine dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps the actions group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [actions/upload-artifact](https://github.com/actions/upload-artifact), [github/codeql-action](https://github.com/github/codeql-action) and [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials). Updates `actions/checkout` from 6 to 7 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v6...v7) Updates `actions/upload-artifact` from 4 to 7 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v7) Updates `github/codeql-action` from 3 to 4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@v3...v4) Updates `aws-actions/configure-aws-credentials` from 4 to 6 - [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases) - [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md) - [Commits](aws-actions/configure-aws-credentials@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions - dependency-name: aws-actions/configure-aws-credentials dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [@spartan-ng/cli](https://github.com/spartan-ng/spartan) from 0.0.1-alpha.720 to 1.0.4. - [Release notes](https://github.com/spartan-ng/spartan/releases) - [Changelog](https://github.com/spartan-ng/spartan/blob/main/CHANGELOG.md) - [Commits](https://github.com/spartan-ng/spartan/commits/v1.0.4) --- updated-dependencies: - dependency-name: "@spartan-ng/cli" dependency-version: 1.0.4 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Verifies the roster hides when empty, renders one avatar per participant under the cap, collapses the rest into a "+N" bubble past the cap, and exposes an accessible viewer-count label.
In the tighter session-bar layout the "{n} viewing" label could wrap
to two lines. whitespace-nowrap keeps it on a single line so the roster
stays visually balanced next to the timer and controls.
feat(billing): subscription, usage and plan management UI
…lop/actions-3aa25f1076 ci(deps): bump the actions group with 4 updates
…-26-alpine ci(deps): bump node from 24-alpine to 26-alpine
…x-1.31-alpine ci(deps): bump nginx from 1.27-alpine to 1.31-alpine
fix(shared): fix typos and double-encoding errors in translation files
apiUrl was never actually read anywhere (HTTP calls use hardcoded relative /api paths), but wsUrl is real and pointed at a domain that doesn't exist. Both empty now: HTTP stays relative, and the WebSocket client falls back to the current origin. CloudFront (reqsai-infra) proxies /api/* and /ws/* to the backend under the same origin, so no absolute URL is needed.
fix: sync prod environment URL fix into develop
The session bar overflowed on small screens ("Recording 1:48 ||
Pause [] Stop" is too wide to fit). The status text is now hidden
below sm: (the pulse color already conveys recording vs. paused) and
the Pause/Resume/Stop buttons collapse to icon-only, following the
same hidden/sm:inline pattern already used by the discovery header's
history/panel-toggle buttons. Adds aria-label/title so the icon-only
buttons stay accessible.
Verifies pause/stop keep an aria-label and title once their visible text is hidden, the hidden/sm:inline classes are actually present on the collapsing labels, the icon and timer always render, and the resume button replaces pause once the session is paused.
The header and the session bar both rendered app-active-participants, so anyone (owner included) saw the roster duplicated. The session bar is the more contextual spot — it's the one place already scoped to "a session is live" — so the header copy is removed.
The pill had asymmetric padding (pl-1.5 vs pr-2.5), crowding the green live-pulse dot against the rounded edge. Matches the right padding.
app-session-bar only renders for users with record permission (canRecord() && recording.isActive()); a plain member (or an owner without that permission) falls into the else-branch banner instead, which never included the roster. Moving it out of session-bar to the page header — rendered unconditionally for every role — fixes the regression where members lost the roster entirely after the previous "remove duplicate" fix.
The avatar stack only ever names people via native title tooltips on the visible avatars, which breaks down past the "+N" cap and doesn't work at all on touch. Clicking (or, on desktop, hovering) the pill now opens a CDK-overlay popover listing every participant with avatar + name, scrollable past a handful of viewers. Closes on outside click, Escape, or shortly after the pointer leaves (grace period so moving from the trigger into the panel doesn't flicker-close it).
Verifies the panel is closed by default and click opens it listing every participant (not just the ones collapsed into the avatar stack), a second click closes it, hover opens it without a click, and mouseleave closes it only after the grace-period delay.
Org admins can now GET the organization, so the general settings page loads for them but stays editable only by the owner. Non-owners see the fields disabled, the per-field Save controls hidden, and an inline hint; the owner flow is unchanged. Ownership is derived from the org ownerId vs the authenticated user id.
The issue-type select is now disabled with a hint until a Jira project is chosen, and its options are keyed by the unique issue-type id (deduped by name) so it binds to the id and displays the name — avoiding duplicate select keys (NG0955) when Jira returns types that share a name. The saved target still stores the issue-type name, resolved from the selected id. The GET target 404 stays handled silently as the unconfigured state. Adds an optional disabled/disabledLabel input to the shared Select.
…ntegrations Normalizes long template lines flagged by the formatter; no behavior change.
Adds an "Import from Jira" action on the backlog next to "Push all to Jira". It previews the candidate Jira issues, opens a picker modal that flags duplicates (already-mapped) with a badge and pre-selects only the non-duplicates, then imports the chosen keys and toasts an imported/skipped/failed summary before refreshing the list. An unconfigured target points the user at project settings. Adds the import models + IntegrationsApiService preview/import methods, pure defaultImportSelection/summarizeImport helpers with unit tests, the integrations.import.* strings (EN+ES) and the JIRA_IMPORT_FAILED error.
…ns service Normalizes long lines flagged by the formatter; no behavior change.
…s configured The backlog's 'Import from Jira' and 'Push all to Jira' buttons rendered enabled even when the project had no Jira target and only failed on click; they now resolve the target on load (the 404 is the expected not-configured answer) and stay disabled with an explanatory tooltip until a mapping exists.
…ayer
Add deleteStory (DELETE .../stories/{id} -> 204) and batchDeleteStories
(POST .../stories/batch-delete -> { deleted }) to DiscoveryApiService, plus
the matching request/result models. Extend IntegrationsApiService.pushAllStories
to take an optional { storyIds } body so a selection can be pushed while the
no-arg call still pushes every eligible story. Covered by HttpTestingController
specs.
Add a per-page selection to the backlog table: a checkbox per row plus a select-all/indeterminate header checkbox (reusing the Indeterminate directive). Selection is a signal<ReadonlySet<string>> scoped to the visible page and cleared on page/filter/sort change. When rows are selected a contextual action bar shows the count, a clear button, 'Push to Jira (n)' (reuses the async job flow with an optional storyIds body) and 'Delete (n)' (confirm modal -> batchDeleteStories -> toast + reload). Adds a row-level trash action with a single-delete confirm modal. Selection toggle/select-all/indeterminate logic lives in a pure, unit-tested selection.helpers module.
A Delete button next to Push-to-Jira opens a confirm modal; on the 204 it toasts and navigates back to the project backlog. Failures keep the page open with a localized error toast.
feat(integrations): Jira integration UI — OAuth connect, project mapping & story push
CI failed at 'bun install --frozen-lockfile' because the grouped dependabot bump (@angular/cdk 22.0.4, @playwright/test 1.61.1, typescript-eslint 8.63.0) updated package.json but left bun.lock out of sync. Regenerated the lockfile; lint, the 189 Vitest tests and the production build all pass with the new versions.
…p/minor-and-patch-85c04f7892 build(deps): bump the minor-and-patch group across 1 directory with 3 updates
refactor(billing): refactor billing plan prices according to landing …
feature/billing-ui (subscription/usage/plan management, Stripe checkout return landings) and bugfix/billing-prices had no CHANGELOG entry.
…leased docs: document Stripe billing UI in CHANGELOG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merges
developintomain, companion release to reqsai-api#71. Billing/Stripe UI code was already onmainfrom an earlier merge — this release brings:PRESENCE_STATEWebSocket event.[Unreleased]entry that was missing for the Stripe billing UI (feature/billing-ui,bugfix/billing-prices).hotfix/sync-websocket-url-develop(fix: sync prod environment URL fix into develop #25) is already reflected onmain(it wasdevelopcatching up to a hotfix applied directly tomain) — no drift expected there.Test plan
https://app.tamci.apploads and logs in cleanly after rollout