Conversation
…er when usage consent overlay is visible
📝 WalkthroughWalkthroughA usage consent flow is introduced to gate AI chat interactions. The ChatWindow now displays a UsageConsentOverlay that must be accepted before users can interact with the ChatInput. This includes disabling the input until consent is given, adding visual consent backdrops, and supporting translations across eight languages. Changes
Sequence DiagramsequenceDiagram
participant User
participant ChatWindow
participant UsageConsentOverlay
participant ChatInput
participant LocationConsentPopup
User->>ChatWindow: Open chat
ChatWindow->>UsageConsentOverlay: Display overlay (usageConsentAccepted=false)
UsageConsentOverlay->>User: Show usage consent prompt
ChatWindow->>ChatInput: Disable input
User->>UsageConsentOverlay: Click "I understand"
UsageConsentOverlay->>ChatWindow: onAccept callback
ChatWindow->>ChatWindow: Set usageConsentAccepted=true
ChatWindow->>ChatInput: Enable input
ChatWindow->>ChatInput: Remove consent backdrop
ChatInput->>User: Input becomes interactive
User->>ChatInput: Type message
User->>ChatInput: Send message
ChatInput->>ChatWindow: onSendMessage
ChatWindow->>ChatWindow: Check usageConsentAccepted (true)
ChatWindow->>ChatWindow: Process message
alt Location consent needed
ChatWindow->>LocationConsentPopup: Display location consent
LocationConsentPopup->>User: Show location request
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.scss (1)
18-27:transition: filterdeclared but nofilterproperty is ever applied.The
--disabledmodifier only setspointer-events: none. Thefiltertransition on line 18 has no correspondingfilterchange to animate. This is harmless but dead code — either add a visual filter (e.g.,filter: grayscale(1)oropacity(0.6)) to the disabled state or remove the transition.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.scss` around lines 18 - 27, The SCSS contains a dead transition: "transition: filter 0.2s ease" but no filter is ever changed; update the &--disabled modifier (the ChatInput disabled selector) to include a visual filter (e.g., add "filter: grayscale(1)" or "filter: opacity(0.6)" on the &--disabled selector) so the transition animates, or remove the "transition: filter 0.2s ease" line entirely; locate the transition and the &--disabled selector in ChatInput.scss and apply one of these fixes to eliminate the dead code.packages/map-template/src/components/ChatWindow/ChatWindow.jsx (2)
90-92: Nit:setUsageConsentAcceptedis a stable React state setter — no need in the dependency array.This is harmless, but the setter from
useStateis guaranteed stable and doesn't need to be listed in theuseCallbackdependency array.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/map-template/src/components/ChatWindow/ChatWindow.jsx` around lines 90 - 92, The useCallback for handleDisclaimerAccept includes setUsageConsentAccepted in its dependency array even though React guarantees state setters are stable; remove setUsageConsentAccepted from the dependency array so handleDisclaimerAccept is created with an empty dependency array (i.e., useCallback(() => { setUsageConsentAccepted(true); }, [])); keep the function body as-is and only adjust the dependency list to exclude the setter.
73-74: Usage consent resets on every remount — verify this is the desired UX.Since
usageConsentAcceptedis local state (useState), users will see the consent overlay every time they open the chat (e.g., close and reopen). If the intent is to persist consent for the session, consider usingsessionStorageor a Recoil atom (similar touserLocationShareState). If the re-prompt on every open is intentional, this is fine as-is.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/map-template/src/components/ChatWindow/ChatWindow.jsx` around lines 73 - 74, The usageConsentAccepted state currently uses local React state (const [usageConsentAccepted, setUsageConsentAccepted]) which resets on remount; to persist consent for the session, initialize the state from sessionStorage (e.g., read sessionStorage.getItem('usageConsentAccepted') on mount) and update sessionStorage whenever setUsageConsentAccepted is called, or replace this local state with a Recoil atom similar to userLocationShareState and store the boolean there; update the ChatWindow handlers that call setUsageConsentAccepted to also persist the value so the overlay no longer re-prompts on every reopen.packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.jsx (1)
13-37: Consider adding accessibility attributes for the modal-like overlay.This overlay gates interaction with the chat, functioning as a modal dialog. Adding
role="dialog",aria-modal="true", andaria-labelledbypointing to the title element would improve screen reader support.♿ Suggested accessibility improvement
- <div className="usage-consent-overlay"> - <div className="usage-consent-overlay__card"> - <h3 className="usage-consent-overlay__title"> + <div className="usage-consent-overlay" role="dialog" aria-modal="true" aria-labelledby="usage-consent-title"> + <div className="usage-consent-overlay__card"> + <h3 id="usage-consent-title" className="usage-consent-overlay__title">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.jsx` around lines 13 - 37, The UsageConsentOverlay component should be treated as a modal: add role="dialog" and aria-modal="true" to the container div (the element returned by UsageConsentOverlay) and add aria-labelledby referencing the title element; give the title element (currently with class usage-consent-overlay__title) a stable id (e.g., usage-consent-title) and set aria-labelledby on the dialog container to that id; ensure the accept/decline buttons (which call onAccept and onDecline) remain focusable and preserve existing handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.jsx`:
- Around line 79-85: The close button in ChatInput.jsx is still clickable even
when the consent overlay is active because tabIndex only affects keyboard focus
and onClick={onClose} lacks a disabled guard; update the button to prevent mouse
clicks when disabled by either making the click handler conditional (e.g., set
onClick to undefined when the disabled prop is true) or ensure the consent
overlay blocks pointer events (e.g., toggle pointer-events on the
overlay/backdrop). Locate the chat-input__close-button and its onClick handler
(onClose) and implement the disabled check against the disabled prop so clicking
the button cannot close the overlay when disabled.
In
`@packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.scss`:
- Around line 41-78: The consent buttons (.usage-consent-overlay__button and its
modifiers &--decline and &--accept) lack :focus-visible styles, leaving keyboard
users without a focus indicator; add explicit :focus-visible rules for both
&--decline and &--accept that provide a high-contrast visible outline (or
box-shadow) and an appropriate outline-offset consistent with the hover/active
states so keyboard navigation is clear and accessible; use :focus-visible (not
:focus) to avoid styling mouse focus, and ensure the focus style contrasts with
each button background and matches the existing design tokens (e.g.,
chat-window-primary-color and gray borders).
In `@packages/map-template/src/i18n/de.js`:
- Around line 97-106: Decide whether the product name should remain untranslated
or be translated, then make the German locale consistent: update the entries
with keys 'Usage consent title', 'Usage consent message', 'AI disclaimer', and
'Location consent message' to either all contain the English brand "Ask with AI"
or all use the German translation "Mit KI fragen" to match the other existing
string (the earlier key that currently uses "Mit KI fragen"); apply the same
choice to the other German translation key that currently differs so every
occurrence in packages/map-template/src/i18n/de.js uses the same form.
---
Duplicate comments:
In `@packages/map-template/src/i18n/nl.js`:
- Around line 97-106: Several translations in this NL file use an inconsistent
brand string for the product (e.g., "Ask with AI")—align these text values with
the canonical brand used throughout the codebase by updating the relevant keys
('Usage consent title', 'Usage consent message', 'Usage consent accept', 'Usage
consent decline', 'AI disclaimer', 'Location consent message', 'Location consent
accept', 'Location consent decline') so they exactly match the agreed brand
wording/format (same casing, spacing and punctuation) used in other locale files
(as flagged in de.js); ensure no other variations remain in these message
values.
In `@packages/map-template/src/i18n/zh.js`:
- Around line 96-105: The translation uses the English brand "Ask with AI" in
several consent entries; update those entries to use the same Chinese brand used
elsewhere ("询问 AI") for consistency. Specifically, in the entries with keys
'Usage consent title', 'Usage consent message', 'AI disclaimer', and 'Location
consent message' replace "Ask with AI" with "询问 AI" and ensure any surrounding
phrasing remains grammatically correct in Chinese (e.g., "询问 AI - 使用须知", "询问 AI
可能会出错", etc.). Verify 'Usage consent accept'/'decline' values remain unchanged.
---
Nitpick comments:
In `@packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.scss`:
- Around line 18-27: The SCSS contains a dead transition: "transition: filter
0.2s ease" but no filter is ever changed; update the &--disabled modifier (the
ChatInput disabled selector) to include a visual filter (e.g., add "filter:
grayscale(1)" or "filter: opacity(0.6)" on the &--disabled selector) so the
transition animates, or remove the "transition: filter 0.2s ease" line entirely;
locate the transition and the &--disabled selector in ChatInput.scss and apply
one of these fixes to eliminate the dead code.
In `@packages/map-template/src/components/ChatWindow/ChatWindow.jsx`:
- Around line 90-92: The useCallback for handleDisclaimerAccept includes
setUsageConsentAccepted in its dependency array even though React guarantees
state setters are stable; remove setUsageConsentAccepted from the dependency
array so handleDisclaimerAccept is created with an empty dependency array (i.e.,
useCallback(() => { setUsageConsentAccepted(true); }, [])); keep the function
body as-is and only adjust the dependency list to exclude the setter.
- Around line 73-74: The usageConsentAccepted state currently uses local React
state (const [usageConsentAccepted, setUsageConsentAccepted]) which resets on
remount; to persist consent for the session, initialize the state from
sessionStorage (e.g., read sessionStorage.getItem('usageConsentAccepted') on
mount) and update sessionStorage whenever setUsageConsentAccepted is called, or
replace this local state with a Recoil atom similar to userLocationShareState
and store the boolean there; update the ChatWindow handlers that call
setUsageConsentAccepted to also persist the value so the overlay no longer
re-prompts on every reopen.
In
`@packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.jsx`:
- Around line 13-37: The UsageConsentOverlay component should be treated as a
modal: add role="dialog" and aria-modal="true" to the container div (the element
returned by UsageConsentOverlay) and add aria-labelledby referencing the title
element; give the title element (currently with class
usage-consent-overlay__title) a stable id (e.g., usage-consent-title) and set
aria-labelledby on the dialog container to that id; ensure the accept/decline
buttons (which call onAccept and onDecline) remain focusable and preserve
existing handlers.
| {/* Remove from tab order while usage consent overlay is visible to prevent accidental closes */} | ||
| <button | ||
| type="button" | ||
| onClick={onClose} | ||
| className="chat-input__close-button" | ||
| aria-label="Close chat" | ||
| tabIndex={disabled ? -1 : 0} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "pointer-events" packages/map-template/src/components/ChatWindow/ChatWindow.scssRepository: MapsPeople/web-ui
Length of output: 88
🏁 Script executed:
rg -n "z-index.*5\|backdrop\|overlay" packages/map-template/src/components/ChatWindow/ChatWindow.scss | head -20Repository: MapsPeople/web-ui
Length of output: 43
🏁 Script executed:
fd "ChatWindow.scss" packages/map-template/src/components/ChatWindow/ -x wc -l {}Repository: MapsPeople/web-ui
Length of output: 127
🏁 Script executed:
cat -n packages/map-template/src/components/ChatWindow/ChatWindow.scssRepository: MapsPeople/web-ui
Length of output: 2418
🏁 Script executed:
rg -n "disabled\|consent\|overlay" packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.jsx -A 2 -B 2 | head -40Repository: MapsPeople/web-ui
Length of output: 43
🏁 Script executed:
fd "ChatWindow.jsx" packages/map-template/src/components/ChatWindow/ -x wc -l {}Repository: MapsPeople/web-ui
Length of output: 127
🏁 Script executed:
rg -n "disabled\|consent" packages/map-template/src/components/ChatWindow/ChatWindow.jsx -A 3 -B 1 | head -50Repository: MapsPeople/web-ui
Length of output: 43
🏁 Script executed:
rg -n "onClose\|onClick" packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.jsx -A 3 -B 1Repository: MapsPeople/web-ui
Length of output: 43
🏁 Script executed:
cat -n packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.jsx | sed -n '70,95p'Repository: MapsPeople/web-ui
Length of output: 1128
🏁 Script executed:
cat -n packages/map-template/src/components/ChatWindow/ChatWindow.jsx | head -100Repository: MapsPeople/web-ui
Length of output: 5270
🏁 Script executed:
rg -n "ChatInput" packages/map-template/src/components/ChatWindow/ChatWindow.jsx -A 8 -B 2Repository: MapsPeople/web-ui
Length of output: 1562
🏁 Script executed:
cat packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.jsxRepository: MapsPeople/web-ui
Length of output: 1748
🏁 Script executed:
cat packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.scssRepository: MapsPeople/web-ui
Length of output: 2368
🏁 Script executed:
rg -n "pointer-events" packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.scssRepository: MapsPeople/web-ui
Length of output: 43
Close button is clickable while consent overlay is active — this is a functional issue.
The tabIndex={disabled ? -1 : 0} only removes keyboard access; it does not prevent mouse clicks. The onClick={onClose} handler has no guard against the disabled prop, so users can dismiss the consent overlay by clicking the X button, which defeats the purpose of requiring explicit Accept/Decline action.
The backdrop (z-index: 5) and overlay (z-index: 6) do not have pointer-events: none, so they do not block clicks from reaching the close button below. Either add pointer-events: none to the overlay when it's active, or guard the onClose handler with a disabled check: onClick={disabled ? undefined : onClose}.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/map-template/src/components/ChatWindow/ChatInput/ChatInput.jsx`
around lines 79 - 85, The close button in ChatInput.jsx is still clickable even
when the consent overlay is active because tabIndex only affects keyboard focus
and onClick={onClose} lacks a disabled guard; update the button to prevent mouse
clicks when disabled by either making the click handler conditional (e.g., set
onClick to undefined when the disabled prop is true) or ensure the consent
overlay blocks pointer events (e.g., toggle pointer-events on the
overlay/backdrop). Locate the chat-input__close-button and its onClick handler
(onClose) and implement the disabled check against the disabled prop so clicking
the button cannot close the overlay when disabled.
| .usage-consent-overlay__button { | ||
| padding: var(--spacing-x-small) var(--spacing-medium); | ||
| border-radius: var(--rounding-large); | ||
| font-size: var(--font-size-small); | ||
| font-weight: var(--font-weight-medium); | ||
| cursor: pointer; | ||
| transition: background-color 0.2s ease, border-color 0.2s ease; | ||
|
|
||
| &--decline { | ||
| background: var(--tailwind-colors-white); | ||
| border: 1px solid #E6E6E6; | ||
| color: var(--color-gray-70); | ||
|
|
||
| &:hover { | ||
| background-color: var(--tailwind-colors-gray-50); | ||
| border-color: var(--tailwind-colors-gray-200); | ||
| color: var(--color-black-black); | ||
| } | ||
|
|
||
| &:active { | ||
| background-color: var(--tailwind-colors-gray-100); | ||
| } | ||
| } | ||
|
|
||
| &--accept { | ||
| background-color: var(--chat-window-primary-color, #005655); | ||
| border: 1px solid var(--chat-window-primary-color, #005655); | ||
| color: var(--color-white-white); | ||
|
|
||
| &:hover { | ||
| opacity: 0.9; | ||
| } | ||
|
|
||
| &:active { | ||
| opacity: 0.8; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Missing :focus-visible styles on consent buttons.
The accept and decline buttons have :hover and :active states but no :focus-visible outline. Keyboard users won't see any focus indicator, which is an accessibility gap for an interactive consent flow.
♿ Proposed fix — add focus-visible styles
.usage-consent-overlay__button {
padding: var(--spacing-x-small) var(--spacing-medium);
border-radius: var(--rounding-large);
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease;
+
+ &:focus-visible {
+ outline: 2px solid var(--chat-window-primary-color, `#005655`);
+ outline-offset: 2px;
+ }
&--decline {📝 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.
| .usage-consent-overlay__button { | |
| padding: var(--spacing-x-small) var(--spacing-medium); | |
| border-radius: var(--rounding-large); | |
| font-size: var(--font-size-small); | |
| font-weight: var(--font-weight-medium); | |
| cursor: pointer; | |
| transition: background-color 0.2s ease, border-color 0.2s ease; | |
| &--decline { | |
| background: var(--tailwind-colors-white); | |
| border: 1px solid #E6E6E6; | |
| color: var(--color-gray-70); | |
| &:hover { | |
| background-color: var(--tailwind-colors-gray-50); | |
| border-color: var(--tailwind-colors-gray-200); | |
| color: var(--color-black-black); | |
| } | |
| &:active { | |
| background-color: var(--tailwind-colors-gray-100); | |
| } | |
| } | |
| &--accept { | |
| background-color: var(--chat-window-primary-color, #005655); | |
| border: 1px solid var(--chat-window-primary-color, #005655); | |
| color: var(--color-white-white); | |
| &:hover { | |
| opacity: 0.9; | |
| } | |
| &:active { | |
| opacity: 0.8; | |
| } | |
| } | |
| } | |
| .usage-consent-overlay__button { | |
| padding: var(--spacing-x-small) var(--spacing-medium); | |
| border-radius: var(--rounding-large); | |
| font-size: var(--font-size-small); | |
| font-weight: var(--font-weight-medium); | |
| cursor: pointer; | |
| transition: background-color 0.2s ease, border-color 0.2s ease; | |
| &:focus-visible { | |
| outline: 2px solid var(--chat-window-primary-color, `#005655`); | |
| outline-offset: 2px; | |
| } | |
| &--decline { | |
| background: var(--tailwind-colors-white); | |
| border: 1px solid `#E6E6E6`; | |
| color: var(--color-gray-70); | |
| &:hover { | |
| background-color: var(--tailwind-colors-gray-50); | |
| border-color: var(--tailwind-colors-gray-200); | |
| color: var(--color-black-black); | |
| } | |
| &:active { | |
| background-color: var(--tailwind-colors-gray-100); | |
| } | |
| } | |
| &--accept { | |
| background-color: var(--chat-window-primary-color, `#005655`); | |
| border: 1px solid var(--chat-window-primary-color, `#005655`); | |
| color: var(--color-white-white); | |
| &:hover { | |
| opacity: 0.9; | |
| } | |
| &:active { | |
| opacity: 0.8; | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/map-template/src/components/ChatWindow/UsageConsentOverlay/UsageConsentOverlay.scss`
around lines 41 - 78, The consent buttons (.usage-consent-overlay__button and
its modifiers &--decline and &--accept) lack :focus-visible styles, leaving
keyboard users without a focus indicator; add explicit :focus-visible rules for
both &--decline and &--accept that provide a high-contrast visible outline (or
box-shadow) and an appropriate outline-offset consistent with the hover/active
states so keyboard navigation is clear and accessible; use :focus-visible (not
:focus) to avoid styling mouse focus, and ensure the focus style contrasts with
each button background and matches the existing design tokens (e.g.,
chat-window-primary-color and gray borders).
| // Ask with AI - Usage Consent | ||
| 'Usage consent title': 'Ask with AI - Nutzungszustimmung', | ||
| 'Usage consent message': 'Ask with AI hilft Ihnen, Orte zu finden und sich in diesem Gebäude mithilfe von Künstlicher Intelligenz (KI) zurechtzufinden. Obwohl wir nach Genauigkeit streben, kann die KI manchmal Fehler machen. Bitte überprüfen Sie wichtige Details, bevor Sie losgehen.', | ||
| 'Usage consent accept': 'Ich verstehe', | ||
| 'Usage consent decline': 'Ablehnen', | ||
| 'AI disclaimer': 'Ask with AI kann Fehler machen. Überprüfen Sie wichtige Details, bevor Sie losgehen.', | ||
| // Location Consent | ||
| 'Location consent message': 'Um personalisierte Wegbeschreibungen bereitzustellen und Ihnen zu helfen, Orte in der Nähe zu entdecken, möchte Ask with AI auf Ihren Standort zugreifen. Der Chat funktioniert auch ohne Ihren Standort, aber Sie müssen dann einen Startpunkt angeben.', | ||
| 'Location consent accept': 'Erlauben', | ||
| 'Location consent decline': 'Ablehnen', |
There was a problem hiding this comment.
Inconsistent use of "Ask with AI" vs. "Mit KI fragen" in German locale.
Line 11 translates "Ask with AI" → "Mit KI fragen", but lines 98, 99, and 102 keep the English "Ask with AI" in the German strings. If "Ask with AI" is the product/brand name that should remain untranslated, then line 11 should also use it. Otherwise, these new strings should use "Mit KI fragen" for consistency.
#!/bin/bash
# Check if other non-English locales also mix "Ask with AI" with translated text
rg -n "Ask with AI" --type=js packages/map-template/src/i18n/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/map-template/src/i18n/de.js` around lines 97 - 106, Decide whether
the product name should remain untranslated or be translated, then make the
German locale consistent: update the entries with keys 'Usage consent title',
'Usage consent message', 'AI disclaimer', and 'Location consent message' to
either all contain the English brand "Ask with AI" or all use the German
translation "Mit KI fragen" to match the other existing string (the earlier key
that currently uses "Mit KI fragen"); apply the same choice to the other German
translation key that currently differs so every occurrence in
packages/map-template/src/i18n/de.js uses the same form.
Summary by CodeRabbit
New Features
Improvements