Skip to content

Add adaptive guided tours to the new UI - #2506

Open
niemyjski wants to merge 57 commits into
mainfrom
feature/ui-guided-tours-review
Open

Add adaptive guided tours to the new UI#2506
niemyjski wants to merge 57 commits into
mainfrom
feature/ui-guided-tours-review

Conversation

@niemyjski

@niemyjski niemyjski commented Aug 20, 2026

Copy link
Copy Markdown
Member

Context

Replacement for #2458, reverted by #2505. Do not merge without Blake's explicit approval.

What changed

  • Adds resource-scoped guided tours to the Svelte UI with compact, numeric per-user status/version progress.
  • Uses a self-only progress endpoint with atomic version/completion precedence. Optional usage events use the existing Exceptionless client; analytics failures do not block progress.
  • Adds global-admin activity charts with Last 30 days, month, and retained-history filters. Counts represent activity, not unique people or conversion rates. Invitations show Shown/Accepted/Dismissed; guides show Started/Completed/Dismissed and entry-point counts/shares. The admin activity page is backed by optional product-tour.* feature-usage telemetry in the internal project. It reports recorded activity, not the saved per-user User.ProductTours state. Saved progress continues to work when PUBLIC_EXCEPTIONLESS_API_KEY is unset, but no new activity appears in the charts until client telemetry is configured.
  • Coordinates catalog, Help, command palette, mobile navigation, keyboard controls, reduced motion, and focus restoration.

Query design

  • Standard time expressions resolved on the backend, with existing start/end filters preserved; the repository date-aggregation context selects histogram intervals. No tour-specific client date-calculation helper.
  • One bounded source/date aggregation with inferred fields and coalesced event sums. History first looks up the earliest retained matching event, then uses that date for the aggregation. These two dependent queries are sequential, not parallel; there are no per-guide queries or user scans.
  • No raw Elasticsearch hooks, prefix queries, hard-coded keyword fields, new analytics index, or refresh-for-consistency requests.

Safety and compatibility

  • Existing application telemetry behavior and retention apply; this is not an anonymous server-side collector.
  • No automatic destructive project, saved-view, stack, Exie prompt, or provider action.
  • No AppHost, legacy Angular, shared theme, API-key, or MIME-type changes.
  • Includes the narrowly scoped shared-filter hydration correction requested during review, with duplicate-filter and open-picker regression coverage. Stored filter models and query values are unchanged.
  • No breaking public API changes.

Verification

Current exact-head verification and review dispositions are maintained in the review summary. All 58 review threads are addressed and resolved. On e58ee7774: Release build and 3,004 backend tests pass, including legacy persisted-status compatibility. The unchanged frontend passed its production build, validation, 844 unit tests, and all 15 affected browser/accessibility cases. Hosted checks are running. Luna high's prior independent pass covered 87 focused tests/type checks. Ready for review; not merged or deployed.

@niemyjski
niemyjski marked this pull request as draft August 20, 2026 14:45
@niemyjski niemyjski self-assigned this Aug 25, 2026
@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch 7 times, most recently from b22a86a to db17534 Compare September 3, 2026 03:45
@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch from c3d3fb5 to 9546c7f Compare September 4, 2026 19:33
Comment thread src/Exceptionless.Core/Models/Data/ProductTourProgress.cs Outdated
Comment thread src/Exceptionless.Core/Models/Data/ProductTours.cs Outdated
Comment thread src/Exceptionless.Core/Models/User.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/Interfaces/IEventRepository.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/EventRepository.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/ProductTourUsageResult.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/UserRepository.cs Outdated
Comment thread src/Exceptionless.Web/Api/Endpoints/AdminEndpoints.cs Outdated
Comment thread src/Exceptionless.Web/Api/Endpoints/AdminEndpoints.cs Outdated
Comment thread src/Exceptionless.Web/Api/Endpoints/UserEndpoints.cs Outdated
@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch from 1233eba to 97a1a43 Compare September 5, 2026 16:19
Comment thread src/Exceptionless.Web/Api/Endpoints/UserEndpoints.cs Outdated
Comment thread src/Exceptionless.Web/Api/Handlers/AdminHandler.cs Outdated
Comment thread src/Exceptionless.Web/Api/Handlers/ProductTourActivityHandler.cs Outdated
Comment thread src/Exceptionless.Web/Api/Handlers/ProductTourActivityHandler.cs Outdated
Comment thread src/Exceptionless.Web/Api/Handlers/ProductTourActivityHandler.cs Outdated
Comment thread src/Exceptionless.Web/ClientApp/src/lib/features/product-tours/api.svelte.ts Outdated
Comment thread src/Exceptionless.Web/ClientApp/src/lib/features/product-tours/catalog.ts Outdated
@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch 2 times, most recently from 97c915f to 81599b4 Compare September 5, 2026 18:42
@niemyjski

niemyjski commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Guided-tour review update — September 8

Head: e58ee7774, based on main 5aeca2d89. All 58 inline review threads have replies and are resolved.

This pass

  • Fixed retained-history cutoff to UTC midnight, matching cleanup. A failing repository regression proved the prior query omitted valid events earlier on the first retained day.
  • Reused Exceptionless date comparison helpers and made the future-only empty-range check distinct from invalid input validation.
  • Added the chart slider's Up/Down behavior and boundary coverage. Invitation-only historical Started activity now correctly produces an empty state instead of an all-zero chart.
  • Reused the shared DateTime component for tooltip/table labels, with explicit UTC options and regression coverage preserving every existing default formatting option.
  • Deleted product-tour-usage.ts, its mode union, and browser calendar arithmetic. The picker passes a standard time expression through; the backend resolves dates with DateTimeRange and Elasticsearch selects buckets. Existing start/end callers remain supported. Added server-side rolling/month, February/leap-year, year-boundary, exact event-boundary, and invalid/mixed-filter tests. Updated the prior review reply, which had only moved the conversion rather than removing it.
  • Removed duplicated invitation visibility and successful-save handling from the host, reducing it by 24 lines without a new state layer. The existing identity, storage-denial, and retry guards remain.
  • Corrected the JSON fixture annotation to the existing /* language=json */ convention.
  • Corrected the cache response path: after reading fresh stored progress, refresh both user-ID and email cache entries through the existing repository cache writer. Removed the Cache(false) call and the earlier justification for bypassing refresh. A regression failed before this correction and now verifies two cache hits, no additional misses, and updated progress through both lookup paths.
  • Reproduced first-organization checkpoint loss during both organization refresh and project creation. The route-only timing change was insufficient: organization query updates can change context before the mutation returns. The host now binds the same user's unscoped setup checkpoint to their first organization on the setup route, preserving the current step and existing identity-switch clearing. Added deterministic browser coverage holding both requests. Ordinary setup submission and the shared organization mutation are unchanged.
  • Reproduced deserialization failure for legacy string-valued tour statuses from the earlier merged implementation. Added storage/cache-only compatibility decoding with numeric writes, preserving the strict numeric API input and unchanged OpenAPI schema. The atomic update recognizes legacy completed status and cannot downgrade it. New historical JSON and repository/API regressions cover decoding, current-user loading, numeric output, and precedence. Earlier coverage omitted historical string-valued records.

Reviewed suggestions intentionally not applied

  • Non-nullable request status: retained [Required] on the nullable input so explicit null reaches semantic validation and returns the requested 422 rather than changing binding to 400. Added separate omitted-status and explicit-null tests. Persisted status remains a non-nullable numeric enum; no storage or generated-contract changes were needed.

Verification

  • Release backend build: zero warnings/errors.
  • Full backend suite on the current head: 3,004 passed, three intentional skips, including legacy string progress, server date filtering, cache hits, concurrent progress updates, missing users, and omitted/null status validation. OpenAPI snapshot verified; generated client models remain unchanged.
  • Full frontend suite: 844 passed (four obsolete client date-calculation cases removed and covered server-side). Svelte check: zero errors/warnings. Formatting, ESLint, and production build passed.
  • Luna high independent final pass: 87 focused tests passed; zero type errors/warnings. Independently confirmed the host refactor's gating and successful-only activity behavior.
  • Final rendered browser pass: 15/15 passed against the updated localhost API, covering first-organization creation, all affected tour flows, real API range comparisons, keyboard chart controls/tooltips, mobile, reduced motion, storage denial, invitation retry, identity isolation, and light/dark layouts. Synthetic screenshots remain explicitly labeled. An intermediate run overlapped build-triggered Vite reloads and failed two invitation cases; the final run was sequenced after build completion and passed without changing timeouts or weakening assertions.
  • One initial browser comparison mismatched a period's response and displayed totals; a traced rerun passed. Strengthened response matching to require the selected range's parameters rather than accepting any usage response. The full affected suite then passed with traces; no application delay or relaxed count assertion was added.
  • Hosted checks are running on the current head. The PR remains ready for review as requested. No merge, release, or deployment performed.

The first-day retention and Up/Down failures were missing boundary cases in earlier tests, now covered. Existing dependency advisories on main remain unchanged; this pass adds no dependencies.

@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch 4 times, most recently from 57cb577 to bf2c3ea Compare September 6, 2026 03:01
@niemyjski
niemyjski force-pushed the feature/ui-guided-tours-review branch from dd9df8a to 76ac733 Compare September 8, 2026 05:05
Comment thread src/Exceptionless.Core/Repositories/EventRepository.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/EventRepository.cs Outdated
Comment thread src/Exceptionless.Core/Repositories/UserRepository.cs Outdated
Comment thread src/Exceptionless.Web/ClientApp/src/lib/features/admin/product-tour-usage.ts Outdated
Comment thread tests/Exceptionless.Tests/Serializer/Models/UserSerializerTests.cs Outdated

Copilot AI 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.

🔵 Needs a closer look

Broad cross-stack changes and unresolved moderate findings require final human review.

Review details

Files not reviewed (1)

  • src/Exceptionless.Web/ClientApp/package-lock.json: Generated file

Suppressed comments (2)

src/Exceptionless.Web/ClientApp/src/lib/features/admin/components/product-tour-activity.svelte:47

  • For invitations, started is intentionally not rendered, but it still contributes to total. A period containing only historical prompt-start telemetry therefore shows a chart and zero-valued Shown/Accepted/Dismissed series instead of the empty state. Compute the total from the displayed keys.
    const total = $derived(tour.shown + tour.started + tour.completed + tour.dismissed);

src/Exceptionless.Web/Models/User/UpdateProductTourProgress.cs:10

  • Status is required by validation but nullable in the OpenAPI contract. Consequently the generated client declares status?: null | ProductTourStatus, even though either form receives a 422 response. Make the request contract non-nullable while preserving missing-value validation, and update the handler accordingly.
  • Files reviewed: 103/106 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread src/Exceptionless.Core/Repositories/EventRepository.cs Outdated
@niemyjski
niemyjski marked this pull request as ready for review September 8, 2026 12:43
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T02:48:36.961564Z f992143 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2ef1ac827

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Exceptionless.Core/Repositories/UserRepository.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8bba742ea7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5b45ad1a00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Exceptionless.Core/Models/Data/ProductTourProgress.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 77755fff07

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +9 to +12
public enum ProductTourStatus
{
Completed = 1,
Dismissed = 2

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restore decoding of legacy string tour statuses

Users whose documents were written during the earlier merged-and-reverted tour implementation can still have product_tours.*.status stored as "dismissed" or "completed", but this enum now accepts only numeric JSON, causing deserialization of the entire user document to fail. Fresh evidence in this target is that the later simplification removed ProductTourStatusConverter and its legacy regression tests, undoing the fix acknowledged in the existing thread; retain backward-compatible reading while continuing to write the numeric API/storage format.

AGENTS.md reference: AGENTS.md:L72-L75

Useful? React with 👍 / 👎.


await PatchAsync(userId, patch, options => options.Cache());

var user = await GetByIdAsync(userId, options => options.Cache());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Refresh the user cache from an authoritative read

When an authentication read that started before PatchAsync repopulates the old user after the patch invalidates its cache entries, this cache-enabled read can return that stale document and leave both ID and email lookups stale, so a completed or dismissed tour can reappear. Fresh evidence in this target is that it again uses GetByIdAsync(...Cache()) and has removed the authoritative read plus AddDocumentsToCacheAsync refresh that the existing thread identified as its fix.

AGENTS.md reference: AGENTS.md:L72-L75

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f275caae14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +91 to +92
if (current != null && (current.version > params.version ||
(current.version == params.version && (current.status == params.completedStatus || current.status == params.status)))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve legacy completed status in the atomic patch

When a legacy record has status: "completed" and the user later dismisses a restarted guide at the same version, this predicate compares the stored string only with numeric enum values, falls through to the update branch, and overwrites the completion with Dismissed. Restoring the legacy deserializer identified in the existing review is insufficient because the script operates directly on the persisted source; also recognize the legacy "completed" value here or normalize it before applying completion precedence.

AGENTS.md reference: AGENTS.md:L72-L75

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.Insulation 37% 35% 286
Exceptionless.Web 85% 69% 8179
Exceptionless.Core 76% 68% 10504
Exceptionless.AppHost 38% 41% 147
Summary 79% (26392 / 33419) 68% (12254 / 18085) 19116

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants