Enhance: EM - save customer select contact type -> tabs#6916
Enhance: EM - save customer select contact type -> tabs#6916Bilguun0410 merged 20 commits intomainfrom
Conversation
Reviewer's GuideRefines the messenger widget’s customer notification and intro experiences by replacing the contact-type combobox with tabs (defaulting to email), surfacing configurable greeting/welcome text and contact links (with favicons), adjusting availability formatting, and aligning the in-widget UI with the admin preview components while cleaning out unused/old UI code. Sequence diagram for rendering header contact links with faviconssequenceDiagram
actor User
participant HeaderIntro
participant Tooltip
participant LinkFavicon
participant getGoogleFavicon
participant Avatar
User->>HeaderIntro: Open_messenger_widget
HeaderIntro->>HeaderIntro: Read_messengerData_links
alt links_configured
loop For_each_link_entry
HeaderIntro->>Tooltip: Render_with_key_and_children
Tooltip->>LinkFavicon: Render_url(url)
LinkFavicon->>getGoogleFavicon: getGoogleFavicon(url)
getGoogleFavicon-->>LinkFavicon: faviconUrl
LinkFavicon->>Avatar: Render_with_Image_src(faviconUrl)
Avatar-->>LinkFavicon: Avatar_component_tree
LinkFavicon-->>Tooltip: Avatar_with_favicon
Tooltip-->>User: Show_icon_and_hover_tooltip(key)
end
else no_links
HeaderIntro-->>User: Show_intro_without_contact_links
end
Updated class diagram for messenger components and utilitiesclassDiagram
direction LR
class NotifyCustomerForm {
+NotifyCustomerForm()
-form
-handleSubmit()
-onTypeChange(value string)
}
class Tabs {
+value string
+onValueChange(value string)
+className string
}
class TabsList {
+className string
}
class TabsTrigger {
+value string
+className string
+children
}
class useCreateCustomerForm {
+useCreateCustomerForm()
+form
-defaultValues_type string = "email"
}
class ChatInput {
+ChatInput(className string, otherProps)
-connection
-messengerData
-messages
-isOnline boolean
-requireAuth boolean
-placeholder string
-id
-message
-handleInputChange(event)
-handleSubmit(event)
-isDisabled boolean
-loading boolean
}
class formatOnlineHours {
+formatOnlineHours(onlineHours, showTimezone boolean, timezone string, timezoneLabel string) string
-parseTime(timeString string) hour minute
-formatTimeZoneLabel(timezone string) string
}
class HeaderIntro {
+HeaderIntro()
-connection
-messengerData
-messages
-onlineHours
-showTimezone boolean
-timezone string
-links Record
}
class LinkFavicon {
+LinkFavicon(url string)
-getGoogleFavicon(url string) string
}
class getGoogleFavicon {
+getGoogleFavicon(url string) string
}
class Avatar {
+Avatar(size string, className string)
+Image(src string)
+Fallback()
}
class ConversationDetails {
+ConversationDetails()
-connection
-messengerData
-messagesConfig
-botGreetMessage
-botShowInitialMessage boolean
-defaultLogo string
}
class InitialMessage {
<<enum>>
+WELCOME string
+AWAY string
}
class EMPreviewIntro {
+EMPreviewIntro()
-greeting
-hours
-availabilityMethod string
}
class EMPreviewMessages {
+EMPreviewMessages()
-config
-intro
-greeting
}
class EMPreviewChatInput {
+EMPreviewChatInput()
-appearance
-backgroundStyles
}
%% Relationships
NotifyCustomerForm --> useCreateCustomerForm : uses
NotifyCustomerForm --> Tabs : renders
Tabs --> TabsList : contains
TabsList --> TabsTrigger : contains
useCreateCustomerForm ..> ChatInput : shares_type_default(email)
HeaderIntro ..> formatOnlineHours : calls
HeaderIntro --> LinkFavicon : renders
LinkFavicon --> Avatar : composes
LinkFavicon ..> getGoogleFavicon : duplicates_logic_of
ChatInput ..> InitialMessage : reads_constants
ConversationDetails ..> InitialMessage : reads_constants
ConversationDetails ..> EMPreviewMessages : mirrors_behavior
ConversationDetails ..> EMPreviewIntro : mirrors_behavior
EMPreviewMessages --> EMPreviewChatInput : renders
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed everything up to 07665df in 13 seconds. Click for details.
- Reviewed
446lines of code in11files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_iqNvUrPZnmPIG3Yv
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In
formatOnlineHours, whenonlineHoursis missing or empty,onlineDaysbecomesundefined, which will render as..., undefined.; consider guarding for this case or omitting the day list when there are no entries. - The
getGoogleFaviconhelper is now defined both inline inLinkFaviconand inlib/getGoogleFavicon.ts; it would be cleaner to reuse the shared helper instead of duplicating the implementation. - The large
defaultLogobase64 string inconversation-details.tsxmakes the component harder to read and maintain; consider moving it to a separate constants/assets module and referencing it from there.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `formatOnlineHours`, when `onlineHours` is missing or empty, `onlineDays` becomes `undefined`, which will render as `..., undefined.`; consider guarding for this case or omitting the day list when there are no entries.
- The `getGoogleFavicon` helper is now defined both inline in `LinkFavicon` and in `lib/getGoogleFavicon.ts`; it would be cleaner to reuse the shared helper instead of duplicating the implementation.
- The large `defaultLogo` base64 string in `conversation-details.tsx` makes the component harder to read and maintain; consider moving it to a separate constants/assets module and referencing it from there.
## Individual Comments
### Comment 1
<location> `apps/frontline-widgets/src/app/messenger/components/header.tsx:51-44` </location>
<code_context>
: WelcomeMessage.AVAILABILITY_MESSAGE}{' '}
- {messages?.thank || ''}
+ </div>
+ <div className='flex flex-col gap-1'>
+ {links && <span className="text-muted-foreground font-medium text-xs">Contact us for any questions or concerns.</span>}
+ <div className='flex gap-1'>
+ {
+ Object.entries(links || {})?.map(([key, value]) => (
+ <Tooltip key={key}>
+ <Tooltip.Trigger>
+ <a href={value as string} target="_blank" rel="noopener noreferrer">
+ <LinkFavicon url={value as string} />
+ </a>
+ </Tooltip.Trigger>
+ <Tooltip.Content>
+ {key}
+ </Tooltip.Content>
+ </Tooltip>
+ ))
+ }
+ </div>
</div>
</div>
</code_context>
<issue_to_address>
**suggestion:** Guard the 'Contact us' copy against an empty `links` object.
With the current `links &&` check, the helper text can appear even when `links` is an empty object, leaving a call-to-action without any contact methods. Consider checking that `links` has at least one entry (e.g. `Object.keys(links || {}).length > 0`) before rendering the helper text and icons.
Suggested implementation:
```typescript
<div className="text-muted-foreground font-medium text-sm">
{messages?.greetings?.message || WelcomeMessage.MESSAGE}{'. '}
{onlineHours
? formatOnlineHours({ onlineHours, showTimezone, timezone })
: WelcomeMessage.AVAILABILITY_MESSAGE}{' '}
</div>
<div className='flex flex-col gap-1'>
{Object.keys(links || {}).length > 0 && (
<span className="text-muted-foreground font-medium text-xs">
Contact us for any questions or concerns.
</span>
)}
```
```typescript
<div className='flex flex-col gap-1'>
{Object.keys(links || {}).length > 0 && (
<span className="text-muted-foreground font-medium text-xs">
Contact us for any questions or concerns.
</span>
)}
{Object.keys(links || {}).length > 0 && (
<div className='flex gap-1'>
{Object.entries(links || {}).map(([key, value]) => (
<Tooltip key={key}>
<Tooltip.Trigger>
<a href={value as string} target="_blank" rel="noopener noreferrer">
<LinkFavicon url={value as string} />
</a>
</Tooltip.Trigger>
<Tooltip.Content>
{key}
</Tooltip.Content>
</Tooltip>
))}
</div>
)}
</div>
```
</issue_to_address>
### Comment 2
<location> `apps/frontline-widgets/src/app/messenger/components/chat-input.tsx:16-17` </location>
<code_context>
const { messengerData } = connection.widgetsMessengerConnect || {};
const { messages, isOnline, requireAuth } = messengerData || {};
const placeholder = isOnline
- ? messages?.welcome || InitialMessage.WELCOME
+ ? InitialMessage.WELCOME
: messages?.away || InitialMessage.AWAY;
</code_context>
<issue_to_address>
**question (bug_risk):** Revisiting the removal of the configurable online placeholder message.
This now ignores any configured `messages?.welcome` value in the online state and always shows `InitialMessage.WELCOME`. If custom welcome text is expected to appear in the placeholder, this may be a breaking behavioral change. Please confirm product expectations, and if the decoupling is intentional, consider updating naming or documentation to reduce confusion for admins.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @@ -42,11 +43,29 @@ export const HeaderIntro = () => { | |||
| {messages?.greetings?.title || WelcomeMessage.TITLE} | |||
| </div> | |||
There was a problem hiding this comment.
suggestion: Guard the 'Contact us' copy against an empty links object.
With the current links && check, the helper text can appear even when links is an empty object, leaving a call-to-action without any contact methods. Consider checking that links has at least one entry (e.g. Object.keys(links || {}).length > 0) before rendering the helper text and icons.
Suggested implementation:
<div className="text-muted-foreground font-medium text-sm">
{messages?.greetings?.message || WelcomeMessage.MESSAGE}{'. '}
{onlineHours
? formatOnlineHours({ onlineHours, showTimezone, timezone })
: WelcomeMessage.AVAILABILITY_MESSAGE}{' '}
</div>
<div className='flex flex-col gap-1'>
{Object.keys(links || {}).length > 0 && (
<span className="text-muted-foreground font-medium text-xs">
Contact us for any questions or concerns.
</span>
)} <div className='flex flex-col gap-1'>
{Object.keys(links || {}).length > 0 && (
<span className="text-muted-foreground font-medium text-xs">
Contact us for any questions or concerns.
</span>
)}
{Object.keys(links || {}).length > 0 && (
<div className='flex gap-1'>
{Object.entries(links || {}).map(([key, value]) => (
<Tooltip key={key}>
<Tooltip.Trigger>
<a href={value as string} target="_blank" rel="noopener noreferrer">
<LinkFavicon url={value as string} />
</a>
</Tooltip.Trigger>
<Tooltip.Content>
{key}
</Tooltip.Content>
</Tooltip>
))}
</div>
)}
</div>| const placeholder = isOnline | ||
| ? messages?.welcome || InitialMessage.WELCOME |
There was a problem hiding this comment.
question (bug_risk): Revisiting the removal of the configurable online placeholder message.
This now ignores any configured messages?.welcome value in the online state and always shows InitialMessage.WELCOME. If custom welcome text is expected to appear in the placeholder, this may be a breaking behavioral change. Please confirm product expectations, and if the decoupling is intentional, consider updating naming or documentation to reduce confusion for admins.
|
📝 WalkthroughWalkthroughThis PR enhances the frontline widgets messaging UI with improved welcome messaging displays, contact link rendering with favicon support, and refactored notification forms. It simplifies placeholder logic, introduces favicon utilities, updates preview components to use dynamic greeting data, and adjusts form field defaults. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/frontline-widgets/src/app/messenger/components/header.tsx (1)
37-68:⚠️ Potential issue | 🟠 MajorValidate link protocols and avoid rendering empty contact sections.
hrefis built from untrusted data and can includejavascript:. Also, an empty object still shows the “Contact us” line. Filter entries by allowed protocols and gate rendering on a non-empty list.🛡️ Suggested fix
export const HeaderIntro = () => { const [connection] = useAtom(connectionAtom); const { messengerData } = connection.widgetsMessengerConnect || {}; - const { messages, onlineHours, showTimezone, timezone, links } = messengerData || {}; + const { messages, onlineHours, showTimezone, timezone, links } = messengerData || {}; + + function isAllowedLink(href: string): boolean { + return /^(https?:|mailto:|tel:)/i.test(href); + } + + const linkEntries = Object.entries(links ?? {}).filter( + ([, value]) => typeof value === 'string' && isAllowedLink(value), + ); + const hasLinks = linkEntries.length > 0; return ( <div className="flex flex-col gap-4"> <div className="gap-2 flex flex-col"> @@ - <div className='flex flex-col gap-1'> - {links && <span className="text-muted-foreground font-medium text-xs">Contact us for any questions or concerns.</span>} - <div className='flex gap-1'> - { - Object.entries(links || {})?.map(([key, value]) => ( - <Tooltip key={key}> - <Tooltip.Trigger> - <a href={value as string} target="_blank" rel="noopener noreferrer"> - <LinkFavicon url={value as string} /> - </a> - </Tooltip.Trigger> - <Tooltip.Content> - {key} - </Tooltip.Content> - </Tooltip> - )) - } - </div> - </div> + <div className='flex flex-col gap-1'> + {hasLinks && ( + <span className="text-muted-foreground font-medium text-xs"> + Contact us for any questions or concerns. + </span> + )} + {hasLinks && ( + <div className='flex gap-1'> + {linkEntries.map(([key, value]) => ( + <Tooltip key={key}> + <Tooltip.Trigger> + <a href={value} target="_blank" rel="noopener noreferrer"> + <LinkFavicon url={value} /> + </a> + </Tooltip.Trigger> + <Tooltip.Content> + {key} + </Tooltip.Content> + </Tooltip> + ))} + </div> + )} + </div>frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewIntro.tsx (1)
45-52:⚠️ Potential issue | 🟡 MinorUse
functionkeyword and guard against undefined schedule values; compute schedule once.The
fromandtofields are optional; template strings will render"undefined - undefined"(truthy), so the fallback won't trigger. Additionally, this pure helper should use thefunctionkeyword per style guidelines, and the schedule is computed twice on line 67 when it could be computed once.🔧 Proposed fix
- const getSchedule = (obj: Partial<Record<Weekday | ScheduleDay, { work?: boolean | undefined; from?: string | undefined; to?: string | undefined; }>>) => { - const days = Object.entries(obj).filter(([_, value]) => value.work).map(([key, _]) => key); - const times = Object.entries(obj).filter(([_, value]) => value.work).map(([_, value]) => `${value.from} - ${value.to}`); - return { - days, - times - } - } + function getSchedule( + obj: Partial<Record<Weekday | ScheduleDay, { work?: boolean; from?: string; to?: string }>>, + ) { + const entries = Object.entries(obj).filter( + ([_, value]) => value.work && value.from && value.to, + ); + const days = entries.map(([key]) => key); + const times = entries.map(([_, value]) => `${value.from} - ${value.to}`); + return { days, times }; + }Also compute the schedule once before use on line 67:
- <p className='text-sm text-medium text-accent-foreground'>We're available between {getSchedule(hours?.onlineHours || {}).times[0] || '9.00 am - 5.00 pm'}, {getSchedule(hours?.onlineHours || {}).days.join(', ') || ''}</p> + {(() => { const schedule = getSchedule(hours?.onlineHours || {}); return <p className='text-sm text-medium text-accent-foreground'>We're available between {schedule.times[0] || '9.00 am - 5.00 pm'}, {schedule.days.join(', ') || ''}</p>; })()}
🤖 Fix all issues with AI agents
In `@apps/frontline-widgets/src/app/messenger/components/chat-input.tsx`:
- Around line 16-18: The online placeholder currently always uses
InitialMessage.WELCOME when isOnline is true, ignoring a configured welcome
override; change the placeholder logic in the chat-input.tsx where the const
placeholder is defined so that when isOnline is true it prefers
messages?.welcome (if present) and falls back to InitialMessage.WELCOME, and
when not online it still uses messages?.away || InitialMessage.AWAY; update the
expression using the isOnline check and the messages?.welcome symbol
accordingly.
In
`@apps/frontline-widgets/src/app/messenger/components/conversation-details.tsx`:
- Around line 240-246: The welcome block after BotMessage is rendered
unconditionally; gate it with the same flag (botShowInitialMessage) so the
entire welcome UI (the avatar div and the span that uses messagesConfig?.welcome
|| InitialMessage.WELCOME) only renders when botShowInitialMessage is true.
Locate the JSX in conversation-details.tsx around the BotMessage line and wrap
the subsequent welcome div (or the whole fragment containing BotMessage and the
welcome div) with the botShowInitialMessage check so the setting controls both
BotMessage and the welcome block.
In `@apps/frontline-widgets/src/app/messenger/components/link-favicon.tsx`:
- Around line 3-14: The LinkFavicon component leaves Avatar.Fallback empty,
reimplements getGoogleFavicon, and types url as a required string despite
checking it; update LinkFavicon to import and use the shared getGoogleFavicon
utility (replace the local getGoogleFavicon function), change the prop type to
accept url?: string (or string | undefined) and guard rendering accordingly, and
provide meaningful fallback content inside Avatar.Fallback (e.g., first letter
of hostname, a default icon label, or an explicit placeholder) so users see a
visual when Avatar.Image fails to load; keep references to Avatar.Image,
Avatar.Fallback and the LinkFavicon component when making these edits.
In `@apps/frontline-widgets/src/lib/getGoogleFavicon.ts`:
- Around line 1-2: Remove the duplicated local getGoogleFavicon function from
the LinkFavicon component and import the shared utility getGoogleFavicon
(exported as getGoogleFavicon) instead; update the LinkFavicon file to add an
import for getGoogleFavicon from the module that defines it, delete the local
definition inside the LinkFavicon component, and ensure all uses inside
LinkFavicon call the imported getGoogleFavicon(url) so behavior remains
identical.
In
`@frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewMessages.tsx`:
- Line 93: The paragraph uses a non-existent CSS class 'wrap-break-word' in
EMPreviewMessages.tsx; replace that class with Tailwind's correct utility
'break-words' on the <p> element (the JSX containing
config?.botSetup?.greetingMessage) so the overflow-wrap behavior is applied
properly.
🧹 Nitpick comments (7)
frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewChatInput.tsx (2)
10-14: Remove unusedbackgroundStylesvariable.The
backgroundStylesmemoized value is computed but never applied to any element in the render output. This appears to be leftover code after switching the Button to usebg-primaryclass.♻️ Proposed fix
-import { useMemo } from "react" export const EMPreviewChatInput = () => { const appearance = useAtomValue(erxesMessengerSetupAppearanceAtom) - const backgroundStyles = useMemo(() => { - return { - backgroundColor: appearance?.primary?.DEFAULT || '#000' - } - }, [appearance]) - return (
18-19: Minor: Unnecessary curly braces in placeholder.Per coding guidelines, avoid unnecessary curly braces in JSX attributes when using string literals.
♻️ Proposed fix
- <Input placeholder={'Send message'} className="flex-1 shadow-2xs" /> - <Button size={'icon'} className="shrink-0 size-8 bg-primary"><IconSend /></Button> + <Input placeholder="Send message" className="flex-1 shadow-2xs" /> + <Button size="icon" className="shrink-0 size-8 bg-primary"><IconSend /></Button>apps/frontline-widgets/src/lib/formatOnlineHours.ts (1)
110-112: Error fallback output differs from success path format.The error fallback omits the trailing period and online days list. While acceptable for a fallback, consider whether the period should be added for consistency:
♻️ Optional fix for consistency
return `We're available between ${firstHour.from} and ${firstHour.to}${ showTimezone && timezone ? ` (${formatTimeZoneLabel(timezone)})` : '' - }`; + }.`;frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewMessages.tsx (1)
77-80: Consider conditional rendering for emptyintro.welcome.The div renders even when
intro?.welcomeis undefined or empty, resulting in an empty styled container. Consider conditionally rendering only when content exists.♻️ Proposed fix
- <div className="flex items-start self-end text-right gap-2 flex-row-reverse max-w-2/3 text-xs text-accent-foreground"> - {intro?.welcome} - </div> + {intro?.welcome && ( + <div className="flex items-start self-end text-right gap-2 flex-row-reverse max-w-2/3 text-xs text-accent-foreground"> + {intro.welcome} + </div> + )}apps/frontline-widgets/src/app/messenger/components/conversation-details.tsx (1)
16-18: Rename the global constant to UPPER_SNAKE_CASE.
defaultLogois a module-level constant; rename toDEFAULT_LOGOto match the naming convention.♻️ Suggested change
-const defaultLogo = 'url(data:image/png;base64,...)' +const DEFAULT_LOGO = 'url(data:image/png;base64,...)'- <div className='w-8 h-8 rounded-full bg-contain bg-center bg-primary' style={{ backgroundImage: defaultLogo }} /> + <div className='w-8 h-8 rounded-full bg-contain bg-center bg-primary' style={{ backgroundImage: DEFAULT_LOGO }} />As per coding guidelines: “Use camelCase naming for functions and variables, PascalCase for classes and components, and UPPER_SNAKE_CASE for global constants”.
apps/frontline-widgets/src/app/messenger/components/header.tsx (1)
6-10: Use an absolute import forLinkFavicon.Please switch the new relative import to the project’s absolute alias scheme for consistency.
As per coding guidelines: “Always use absolute paths when importing”.
frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewIntro.tsx (1)
64-68: Compute the schedule once per render.
getSchedule(...)is called twice with the same input. Cache the result to avoid duplicate work and keep the display consistent.♻️ Proposed refactor
const greeting = useAtomValue(erxesMessengerSetupGreetingAtom); const hours = useAtomValue(erxesMessengerSetupHoursAtom); + const schedule = getSchedule(hours?.onlineHours ?? {}); @@ - <p className='text-sm text-medium text-accent-foreground'>We're available between {getSchedule(hours?.onlineHours || {}).times[0] || '9.00 am - 5.00 pm'}, {getSchedule(hours?.onlineHours || {}).days.join(', ') || ''}</p> + <p className='text-sm text-medium text-accent-foreground'> + We're available between {schedule.times[0] || '9.00 am - 5.00 pm'}, {schedule.days.join(', ') || ''} + </p>
| const placeholder = isOnline | ||
| ? messages?.welcome || InitialMessage.WELCOME | ||
| ? InitialMessage.WELCOME | ||
| : messages?.away || InitialMessage.AWAY; |
There was a problem hiding this comment.
Potential regression: online placeholder ignores configured welcome text.
If messages?.welcome is set, the input placeholder will no longer reflect it, which can diverge from the configured welcome messaging used elsewhere. Consider restoring the configured welcome as the preferred online placeholder.
💡 Suggested fix
- const placeholder = isOnline
- ? InitialMessage.WELCOME
- : messages?.away || InitialMessage.AWAY;
+ const placeholder = isOnline
+ ? messages?.welcome || InitialMessage.WELCOME
+ : messages?.away || InitialMessage.AWAY;📝 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.
| const placeholder = isOnline | |
| ? messages?.welcome || InitialMessage.WELCOME | |
| ? InitialMessage.WELCOME | |
| : messages?.away || InitialMessage.AWAY; | |
| const placeholder = isOnline | |
| ? messages?.welcome || InitialMessage.WELCOME | |
| : messages?.away || InitialMessage.AWAY; |
🤖 Prompt for AI Agents
In `@apps/frontline-widgets/src/app/messenger/components/chat-input.tsx` around
lines 16 - 18, The online placeholder currently always uses
InitialMessage.WELCOME when isOnline is true, ignoring a configured welcome
override; change the placeholder logic in the chat-input.tsx where the const
placeholder is defined so that when isOnline is true it prefers
messages?.welcome (if present) and falls back to InitialMessage.WELCOME, and
when not online it still uses messages?.away || InitialMessage.AWAY; update the
expression using the isOnline check and the messages?.welcome symbol
accordingly.
| {botShowInitialMessage && <BotMessage content={botGreetMessage} />} | ||
| <div className='flex justify-start gap-2'> | ||
| <div className='w-8 h-8 rounded-full bg-contain bg-center bg-primary' style={{ backgroundImage: defaultLogo }} /> | ||
| <span className='h-auto font-medium flex flex-col justify-start items-start text-[13px] leading-relaxed text-foreground text-left gap-1 px-3 py-2 bg-background whitespace-break-spaces wrap-break-word break-all rounded-lg shadow-2xs'> | ||
| {messagesConfig?.welcome || InitialMessage.WELCOME} | ||
| </span> | ||
| </div> |
There was a problem hiding this comment.
Welcome block should respect botShowInitialMessage.
The new welcome block renders unconditionally, so it still appears even if botShowInitialMessage is false. Consider gating it to keep the setting authoritative.
💡 Suggested guard
- <div className='flex justify-start gap-2'>
- <div className='w-8 h-8 rounded-full bg-contain bg-center bg-primary' style={{ backgroundImage: defaultLogo }} />
- <span className='h-auto font-medium flex flex-col justify-start items-start text-[13px] leading-relaxed text-foreground text-left gap-1 px-3 py-2 bg-background whitespace-break-spaces wrap-break-word break-all rounded-lg shadow-2xs'>
- {messagesConfig?.welcome || InitialMessage.WELCOME}
- </span>
- </div>
+ {botShowInitialMessage && (
+ <div className='flex justify-start gap-2'>
+ <div className='w-8 h-8 rounded-full bg-contain bg-center bg-primary' style={{ backgroundImage: DEFAULT_LOGO }} />
+ <span className='h-auto font-medium flex flex-col justify-start items-start text-[13px] leading-relaxed text-foreground text-left gap-1 px-3 py-2 bg-background whitespace-break-spaces wrap-break-word break-all rounded-lg shadow-2xs'>
+ {messagesConfig?.welcome || InitialMessage.WELCOME}
+ </span>
+ </div>
+ )}🤖 Prompt for AI Agents
In `@apps/frontline-widgets/src/app/messenger/components/conversation-details.tsx`
around lines 240 - 246, The welcome block after BotMessage is rendered
unconditionally; gate it with the same flag (botShowInitialMessage) so the
entire welcome UI (the avatar div and the span that uses messagesConfig?.welcome
|| InitialMessage.WELCOME) only renders when botShowInitialMessage is true.
Locate the JSX in conversation-details.tsx around the BotMessage line and wrap
the subsequent welcome div (or the whole fragment containing BotMessage and the
welcome div) with the botShowInitialMessage check so the setting controls both
BotMessage and the welcome block.
| export const LinkFavicon = ({ url }: { url: string }) => { | ||
| const getGoogleFavicon = (url: string) => | ||
| `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url}&size=128`; | ||
|
|
||
| return ( | ||
| <Avatar size="lg" className="rounded"> | ||
| {url && ( | ||
| <Avatar.Image src={getGoogleFavicon(url)} /> | ||
| )} | ||
| <Avatar.Fallback></Avatar.Fallback> | ||
| </Avatar> | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Provide meaningful fallback content and use the shared utility.
- The
Avatar.Fallbackis empty, providing no visual feedback if the favicon fails to load. - The
getGoogleFaviconfunction duplicates the utility in@/lib/getGoogleFavicon.ts. - The
urlprop is typed as requiredstring, but the conditional{url && ...}suggests empty strings may be passed.
♻️ Proposed fix
import { Avatar } from 'erxes-ui';
+import { getGoogleFavicon } from '@/lib/getGoogleFavicon';
+import { IconLink } from '@tabler/icons-react';
-export const LinkFavicon = ({ url }: { url: string }) => {
- const getGoogleFavicon = (url: string) =>
- `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url}&size=128`;
-
+export const LinkFavicon = ({ url }: { url?: string }) => {
return (
<Avatar size="lg" className="rounded">
{url && (
<Avatar.Image src={getGoogleFavicon(url)} />
)}
- <Avatar.Fallback></Avatar.Fallback>
+ <Avatar.Fallback>
+ <IconLink className="size-4" />
+ </Avatar.Fallback>
</Avatar>
);
};🤖 Prompt for AI Agents
In `@apps/frontline-widgets/src/app/messenger/components/link-favicon.tsx` around
lines 3 - 14, The LinkFavicon component leaves Avatar.Fallback empty,
reimplements getGoogleFavicon, and types url as a required string despite
checking it; update LinkFavicon to import and use the shared getGoogleFavicon
utility (replace the local getGoogleFavicon function), change the prop type to
accept url?: string (or string | undefined) and guard rendering accordingly, and
provide meaningful fallback content inside Avatar.Fallback (e.g., first letter
of hostname, a default icon label, or an explicit placeholder) so users see a
visual when Avatar.Image fails to load; keep references to Avatar.Image,
Avatar.Fallback and the LinkFavicon component when making these edits.
| export const getGoogleFavicon = (url: string) => | ||
| `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url}&size=128`; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Code duplication: This utility is duplicated in link-favicon.tsx.
The same getGoogleFavicon function is defined locally inside LinkFavicon component at apps/frontline-widgets/src/app/messenger/components/link-favicon.tsx. The component should import this utility instead of duplicating it.
♻️ Update link-favicon.tsx to use this utility
In apps/frontline-widgets/src/app/messenger/components/link-favicon.tsx:
import { Avatar } from 'erxes-ui';
+import { getGoogleFavicon } from '@/lib/getGoogleFavicon';
export const LinkFavicon = ({ url }: { url: string }) => {
- const getGoogleFavicon = (url: string) =>
- `https://t2.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${url}&size=128`;
-
return (🤖 Prompt for AI Agents
In `@apps/frontline-widgets/src/lib/getGoogleFavicon.ts` around lines 1 - 2,
Remove the duplicated local getGoogleFavicon function from the LinkFavicon
component and import the shared utility getGoogleFavicon (exported as
getGoogleFavicon) instead; update the LinkFavicon file to add an import for
getGoogleFavicon from the module that defines it, delete the local definition
inside the LinkFavicon component, and ensure all uses inside LinkFavicon call
the imported getGoogleFavicon(url) so behavior remains identical.
| className="h-auto font-normal flex flex-col justify-start items-start text-left gap-1 p-3" | ||
| > | ||
| <p>{intro?.welcome || "Hi, any questions?"}</p> | ||
| <p className='wrap-break-word'>{config?.botSetup?.greetingMessage || "Hi, any questions?"}</p> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for wrap-break-word definition in CSS/SCSS and tailwind config files
echo "=== Searching for wrap-break-word definition in CSS/SCSS files ==="
rg -n "wrap-break-word" --type css --type scss
echo -e "\n=== Searching in tailwind.config files ==="
fd tailwind.config -x cat -n {}
echo -e "\n=== Searching for wrap-break-word in all files ==="
rg -n "wrap-break-word" -t js -t ts -t json 2>/dev/null | head -20Repository: erxes/erxes
Length of output: 9668
🏁 Script executed:
#!/bin/bash
# Search for CSS files in the project
echo "=== Searching for CSS/global style files ==="
find . -name "*.css" -type f | grep -E "(global|styles|tailwind)" | head -20
echo -e "\n=== Checking for any CSS content with wrap-break-word definition ==="
rg "\.wrap-break-word|wrap-break-word\s*{" --type css -A 2
echo -e "\n=== Checking if it might be defined elsewhere ==="
rg "wrap-break-word" --type css --type js --type ts | grep -E "(:|-\>|=|\{)" | head -10Repository: erxes/erxes
Length of output: 2915
🏁 Script executed:
#!/bin/bash
# Check the actual CSS files to see if wrap-break-word is defined there
echo "=== Content of styles.css files ==="
cat ./apps/frontline-widgets/src/styles.css | head -50
echo -e "\n=== Content of core-ui styles.css ==="
cat ./frontend/core-ui/src/styles.css | head -50Repository: erxes/erxes
Length of output: 2981
Replace wrap-break-word with valid Tailwind CSS class break-words.
wrap-break-word is not defined anywhere in the codebase—not in Tailwind config, CSS files, or any other styling mechanism. This class name will not apply any styles. Use break-words instead, which is Tailwind's standard utility for overflow-wrap: break-word.
🤖 Prompt for AI Agents
In
`@frontend/plugins/frontline_ui/src/modules/integrations/erxes-messenger/components/EMPreviewMessages.tsx`
at line 93, The paragraph uses a non-existent CSS class 'wrap-break-word' in
EMPreviewMessages.tsx; replace that class with Tailwind's correct utility
'break-words' on the <p> element (the JSX containing
config?.botSetup?.greetingMessage) so the overflow-wrap behavior is applied
properly.



Summary by Sourcery
Update messenger customer notification and preview experiences and surface additional configuration-driven messaging and contact options.
New Features:
Enhancements:
Important
Enhance messenger UI with improved chat input, conversation details, header, and notification form, adding link favicons and refining message grouping and online hours formatting.
chat-input.tsx: Changed placeholder logic to always useInitialMessage.WELCOMEwhen online.conversation-details.tsx: Added default logo and welcome message display.header.tsx: Added link favicons and contact links display.notify-customer-form.tsx: Replaced contact type selection with tabs.link-favicon.tsxfor displaying favicons.formatOnlineHours.ts: Enhanced to include days in the formatted string.EMPreviewChatInput.tsxandEMPreviewIntro.tsx.This description was created by
for 07665df. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
Release Notes
New Features
Improvements