Skip to content

fix(widget): make widget-path configurable for NestJS backend#35

Open
mpge wants to merge 5 commits intomainfrom
fix/widget-configurable-path
Open

fix(widget): make widget-path configurable for NestJS backend#35
mpge wants to merge 5 commits intomainfrom
fix/widget-configurable-path

Conversation

@mpge
Copy link
Copy Markdown
Member

@mpge mpge commented Apr 24, 2026

Summary

The shared Vue frontend had /support/widget hardcoded across 6+ places, meaning chat + widget features simply wouldn't work against the NestJS reference backend (which mounts its widget routes at /escalated/widget). Surfaced during continued docs self-review while cross-checking widget API paths against each framework's route registration.

What's fixed

External embeddable widget (EscalatedWidget.vue + index.js):

  • New widgetPath prop, default /support/widget.
  • data-widget-path attribute on the loader script tag (also overridable via window.EscalatedWidget.widgetPath).

useChat composable — hardcoded /support/widget/chat/* in all 6 API methods (start, send, typing, end, rate, availability). Now accepts options.widgetPath, default unchanged.

Agent-UI chat components — now pass widgetPath to useChat() computed from Inertia page props (page.props.escalated?.prefix, the same pattern EscalatedLayout.vue and Admin/Settings.vue already use):

  • pages/Agent/TicketShow.vue — plus fixes the two inline ChatComposer endpoint props that were outside useChat (lines 327-330).
  • components/ActiveChatsPanel.vue
  • components/ChatQueue.vue

Usage

Every non-NestJS backend keeps working unchanged — the default /support/widget is still the default.

NestJS external-widget embedders add one attribute:

<script src="https://yourhost.example/escalated-widget.js"
        data-base-url="https://yourhost.example"
        data-widget-path="/escalated/widget"
        async></script>

NestJS-served agent-UI chat: host app just needs to share escalated.prefix = 'escalated' via Inertia middleware (same shared-data pattern the host already uses for the prefix value consumed by other pages).

Test plan

  • npm run lint clean (existing lint-staged pre-commit hooks also ran on each commit).
  • Manual smoke: embed the widget against both a Laravel and a NestJS backend, verify widget ticket submission works; exercise agent chat UI on both backends, verify messages send/receive correctly.

Remaining scope

  • useMentions.js is already overridable via options.searchEndpoint; just needs host apps to pass the right one (pre-existing pattern, not in this PR).

Follow-up

escalated-docs#10's widget-snippet section already documents data-widget-path for NestJS embedders (added in a follow-up commit on docs/public-tickets).

mpge added 5 commits April 24, 2026 12:16
The shared Vue widget hardcoded `/support/widget` as the API path
prefix, which matches every host-adapter plugin (Laravel, Rails,
Django, Adonis, WordPress, Filament, Symfony, .NET, Go, Spring,
Phoenix). But the NestJS reference mounts its WidgetController at
`/escalated/widget` — so the shared widget simply couldn't talk to
a NestJS backend at all.

Adds:
  - a `widgetPath` prop on EscalatedWidget.vue (default
    `/support/widget`)
  - a `data-widget-path` attribute on the loader script tag (also
    overridable via window.EscalatedWidget.widgetPath)

NestJS embedders add `data-widget-path="/escalated/widget"` to the
script tag; every other framework continues to work unchanged with
the default.

Verified no ESLint warnings on modified files. Widget behavior for
Laravel etc. is unchanged because the default path is `/support/widget`.
Same class of bug as escalated#35 (widget hardcoded /support/widget).
useChat composable hardcoded /support/widget/chat/* URLs in all six
API methods (startChat, sendMessage, sendTyping, endChat, rateChat,
checkAvailability), so chat functionality in agent UI pages
(ActiveChatsPanel, ChatQueue, Agent/TicketShow) can't reach NestJS's
/escalated/widget/chat/* routes.

useChat now accepts options.widgetPath (default /support/widget);
each URL is built from that prefix. Existing callers keep working
unchanged — they call useChat() with no arg and get the default
path.

NestJS hosts can pass useChat({ widgetPath: '/escalated/widget' })
at each call site, OR the host app's Inertia layer can supply it via
a shared context / provide/inject pattern (future refactor).

Verified npm run lint clean.
Wires up the useChat options.widgetPath (added earlier in this PR) in
all three agent-facing callers:

  - pages/Agent/TicketShow.vue — also fixes the two inline ChatComposer
    endpoints (send-endpoint / typing-endpoint) that were hardcoded as
    `/support/widget/chat/...` outside of useChat. Reads the route
    prefix from page.props.escalated?.prefix (same pattern used across
    the shared frontend; default 'support').
  - components/ActiveChatsPanel.vue — imports usePage, reads
    page.props.escalated?.prefix, passes widgetPath to useChat().
  - components/ChatQueue.vue — same pattern.

Combined with the earlier widget + useChat fixes in this PR, chat
functionality now works end-to-end on both default-prefix
('support') and NestJS-prefix ('escalated') host frameworks.

Verified `npm run lint` clean.
The widgetPath change made ActiveChatsPanel call usePage() at setup
time. Without an Inertia context, page.props is undefined, which broke
all 9 ActiveChatsPanel tests in CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant