Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.

Add Chatbot on IssueHub V0.2#26

Open
emylattuada wants to merge 4 commits into
kanywst:mainfrom
emylattuada:main
Open

Add Chatbot on IssueHub V0.2#26
emylattuada wants to merge 4 commits into
kanywst:mainfrom
emylattuada:main

Conversation

@emylattuada

@emylattuada emylattuada commented May 20, 2026

Copy link
Copy Markdown

Add IssueBot – Floating ChatBot (v0.2)

What is this?

This PR introduces IssueBot, a lightweight floating chatbot widget built from scratch and integrated into IssueHub as a client-side React component.

IssueBot was created by students from U.T.U (Universidad del Trabajo del Uruguay) as their first open source contribution, with the goal of making information on the site more accessible — directly through the interface, without having to navigate away.


How it works

A floating bubble sits in the bottom-right corner of every page. Clicking it opens a chat window where users can interact with the bot via predefined commands or quick-action buttons.

The main feature in this version allows users to subscribe for notifications via email, using EmailJS as the delivery layer — no backend required.


Tech stack

  • Originally built with plain HTML, CSS and JavaScript
  • Adapted to the project as a React TSX component
  • Email sending handled by EmailJS (installed as @emailjs/browser package)
  • Styled to match IssueHub's existing dark zinc visual language

Current functionality (v0.2)

  • Floating chat bubble with open/close animation
  • Welcome message with recommended action button
  • Keyword-based responses (hello, hey, ping, today, time, about)
  • rn shortcut triggers the notification flow via text
  • Email subscription flow with zod validation
  • EmailJS integration — sends a default template on subscription

Known limitations / out of scope for this version

  • No email list collection or storage
  • No unsubscribe mechanism
  • Email template is a default test template
  • No backend — this is a fully client-side prototype

Changes from v0.1 — addressing reviewer feedback

  • Mounted correctly<Chatbot /> is now rendered in src/app/layout.tsx
  • Fixed 404 on GitHub Pages — replaced <img src="/Imagenes/..."> with next/image, which correctly applies the /IssueHub basePath
  • Accessibility — bubble and send button changed from <div onClick> to <button type="button">, with proper aria-label attributes
  • EmailJS credentials — keys moved out of hardcoded values; @emailjs/browser added as a proper package dependency instead of a CDN script injection
  • Theme consistency — removed Poppins and white background; now uses Geist font and the dark zinc color palette (#18181b, #27272a) matching the rest of the project
  • today / time fix — values are now computed at call time, not frozen at module load
  • Email validation — replaced includes('@') check with zod (z.string().email()), which is already a project dependency
  • English only — removed Spanish comments and title="Minimizar"; renamed Imagenes/images/
  • @types/react bump — removed from this PR, split out as unrelated

Files changed

src/components/Chatbot/
  Chatbot.tsx
  Chatbot.css
src/app/
  layout.tsx
public/
  images/
    logo.png
    send.png

Notes

This is our first contribution to an open source project. We are open to feedback and any further changes needed to meet the project's standards. Thank you for the detailed review on v0.1 — it helped us improve a lot! 🙏

Summary by CodeRabbit

  • New Features
    • Persistent floating chatbot available across all app pages with launcher bubble
    • Interactive chat UI with message history, auto-scroll, and welcome prompt on first open
    • In-chat email capture flow (with validation) to receive notifications
    • Quick-reply/recommended option to trigger the email flow and guide users

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a persistent floating Chatbot with canned responses, a one-time welcome prompt, an email-capture flow (zod + EmailJS), full UI rendering, and stylesheet; the Chatbot is mounted in the root layout.

Changes

Chatbot Widget Implementation

Layer / File(s) Summary
Chatbot state and initialization
src/components/Chatbot/Chatbot.tsx
Initial response map, message/id types, component state (open, messages, input, email mode), refs, and effects for auto-scroll and injecting the welcome message.
Email flow and send logic
src/components/Chatbot/Chatbot.tsx
Implements zod email validation, dynamic @emailjs/browser import and emailjs.send, optimistic "Sending…" updates, and send handler routing input to email flow or canned responses (handles __EMAIL_FLOW__).
Chat UI and WelcomeMessage component
src/components/Chatbot/Chatbot.tsx
Renders launcher bubble, chat container with header/minimize, scrollable messages list (uses WelcomeMessage), input row with email mode switching, send button, and bubble toggle.
Chatbot visual styling
src/components/Chatbot/Chatbot.css
Full CSS for launcher bubble and animations, open/close icon transitions, main container open animation, header/logo/minimize, scrollable body and custom scrollbar, bot/user bubbles, welcome options, and input/send styling.
Root layout integration
src/app/layout.tsx
Imports Chatbot and renders it inside RootLayout body so the widget appears on all pages.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A little rabbit types with glee, 🐇
Floating chat arrives for free,
Welcome prompts and emails sent,
Styled with care and event by event,
IssueHub listens — that's the key! 💬

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a chatbot (IssueBot) feature to IssueHub in version 0.2, matching the substantial code additions across layout, component, and styling files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: one or more packages not found in the registry.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a new Chatbot component to the application, integrated into the root layout. The implementation includes a dedicated CSS file for styling and a React component that handles user interactions, simple command responses, and an email notification flow using EmailJS and Zod validation. Feedback focuses on critical security improvements by moving hardcoded credentials to environment variables, enhancing maintainability by replacing hardcoded colors with CSS variables and removing magic strings, and improving React patterns by scoping ID generation to the component instance. Additionally, an accessibility improvement was suggested to ensure the chat window behaves correctly as a modal dialog.

Comment on lines +93 to +98
await emailjs.send(
'service_2fc5v5k',
'template_d3d70zp',
{ user_email: email, user: 'IssueHub' },
{ publicKey: 'De3J97q5iJ8M72LB0' }
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-critical critical

Hardcoding credentials like the EmailJS service ID, template ID, and public key directly in the source code is a significant security risk. These values are exposed in the client-side bundle, allowing anyone to potentially misuse them.

These secrets should be moved to environment variables (e.g., a .env.local file) and accessed via process.env. For Next.js, remember to prefix client-side environment variables with NEXT_PUBLIC_.

Note: The suggestion below uses the non-null assertion operator (!). It's crucial to add runtime checks to ensure these environment variables are actually present before calling emailjs.send to avoid runtime errors.

Suggested change
await emailjs.send(
'service_2fc5v5k',
'template_d3d70zp',
{ user_email: email, user: 'IssueHub' },
{ publicKey: 'De3J97q5iJ8M72LB0' }
);
await emailjs.send(
process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID!,
process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID!,
{ user_email: email, user: 'IssueHub' },
{ publicKey: process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY! }
);

Comment on lines +64 to +65
background: #18181b; /* --muted: zinc-900 */
border: 1px solid #27272a; /* --border: zinc-800 */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This component uses hardcoded hex color codes (e.g., #18181b, #27272a). The project defines a theme with CSS variables in globals.css (like --muted and --border) to ensure visual consistency.

To align with the project's design system and improve maintainability, please replace the hardcoded colors in this file with the corresponding CSS variables.

For example:

  • #fafafa should be var(--foreground)
  • #0a0a0b should be var(--background)
  • #18181b should be var(--muted)
  • #27272a should be var(--border)
  • #a1a1aa should be var(--muted-foreground)
Suggested change
background: #18181b; /* --muted: zinc-900 */
border: 1px solid #27272a; /* --border: zinc-800 */
background: var(--muted); /* --muted: zinc-900 */
border: 1px solid var(--border); /* --border: zinc-800 */

Comment on lines +33 to +34
let msgId = 0;
const nextId = () => ++msgId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using a module-level mutable variable (msgId) to generate React keys is not a safe pattern. In React's Strict Mode, components can render twice in development, which would cause this counter to increment unexpectedly and could lead to key-related issues. State for generating IDs should be scoped to the component instance.

Please remove these lines and add the following inside your Chatbot component to manage the ID generation safely:

const msgIdCounter = useRef(0);
const nextId = () => ++msgIdCounter.current;

Comment on lines +124 to +139
setTimeout(() => {
const res = responseObj[text.toLowerCase()];

if (typeof res === 'function') {
const result = res();
if (result === '__EMAIL_FLOW__') {
startEmailFlow();
} else if (typeof result === 'string') {
addMessage(result);
}
} else if (typeof res === 'string') {
addMessage(res);
} else {
addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping, rn.");
}
}, 500);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current logic for triggering the email flow relies on a magic string __EMAIL_FLOW__ which makes the code less direct and harder to maintain. A clearer approach is to handle the rn command explicitly.

I recommend removing the 'rn' entry from responseObj (on line 18) and modifying this block to handle it directly.

    setTimeout(() => {
      const command = text.toLowerCase();

      if (command === 'rn') {
        startEmailFlow();
        return;
      }

      const res = responseObj[command];

      if (typeof res === 'function') {
        const result = res();
        if (typeof result === 'string') {
          addMessage(result);
        }
      } else if (typeof res === 'string') {
        addMessage(res);
      } else {
        addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping.");
      }
    }, 500);

className={`chatbot-container${isOpen ? ' open' : ''}`}
role="dialog"
aria-label="IssueBot chat"
aria-modal="false"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The chat window is marked with role="dialog" but aria-modal="false". While the chat is open, keyboard focus is not trapped within it, allowing users to tab to interactive elements on the page behind the chat window. This can create a confusing experience, especially for users of assistive technologies.

To improve accessibility, this should behave as a modal dialog. Please change aria-modal to "true" and implement focus trapping to keep the user's focus within the chat component while it is open.

Suggested change
aria-modal="false"
aria-modal="true"

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

🧹 Nitpick comments (7)
src/components/Chatbot/Chatbot.tsx (5)

58-58: 💤 Low value

Magic number timeouts lack documentation.

The delays of 260ms (line 58), 50ms (line 77), and 500ms (line 139) have no comments explaining their purpose (e.g., animation timing, perceived responsiveness). This harms maintainability.

Consider extracting these as named constants at the top of the file:

const FOCUS_DELAY_MS = 260; // Wait for open animation
const EMAIL_FOCUS_DELAY_MS = 50;
const BOT_RESPONSE_DELAY_MS = 500; // Simulate typing delay

Also applies to: 77-77, 139-139

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` at line 58, Replace the three
magic-number timeouts used in setTimeout calls (e.g., the call that invokes
inputRef.current?.focus(), the timeout used around EMAIL focus, and the BOT
response delay) with descriptive named constants defined at the top of the
Chatbot component file (for example FOCUS_DELAY_MS, EMAIL_FOCUS_DELAY_MS,
BOT_RESPONSE_DELAY_MS) and add brief comments explaining each constant's purpose
(e.g., "wait for open animation", "small email input focus latency", "simulate
bot typing delay"); then update the setTimeout invocations to use these
constants so the intent is documented and maintainable.

82-84: ⚡ Quick win

Invalid email error does not clear the input field.

When validation fails, the bot shows "Invalid email. Please try again." but leaves the malformed email in the input. Users must manually select and delete it, which is a minor UX friction.

✨ Proposed fix: clear input on validation failure
    const result = emailSchema.safeParse(email);
    if (!result.success) {
+     setInputValue('');
      addMessage('Invalid email. Please try again.');
      return;
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` around lines 82 - 84, The email
validation branch currently shows an error message but leaves the malformed
value in the input; in the handler that checks result.success (likely the email
submit handler in Chatbot component, e.g., handleEmailSubmit or the function
where result and addMessage are used) after calling addMessage('Invalid email.
Please try again.') also clear the input state (call the component's input state
setter such as setInput('') or reset the controlled input value) and ensure any
related validation state is reset so the UI empties the field on validation
failure.

80-109: ⚖️ Poor tradeoff

Email submission lacks rate limiting or abuse prevention.

A user can repeatedly click the send button or use the rn command to trigger unlimited email submissions without throttling, potentially exhausting your EmailJS quota.

Consider adding client-side rate limiting (e.g., disable submission for 60 seconds after success) or a simple cooldown state. For a v0.2 prototype this may be deferred, but plan to add server-side validation and rate limiting before broader release.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` around lines 80 - 109, The submitEmail
flow can be abused by repeated submissions; add a client-side cooldown state
(e.g., emailCooldown boolean or emailCooldownUntil timestamp) checked at the
start of submitEmail to short-circuit and show a message if still cooling down,
set that state when a send succeeds (or on attempt) and use setTimeout or
calculating remaining time to clear it after ~60s, and ensure the UI/send button
is disabled using that state; update places that toggle email mode
(setIsEmailMode, setPlaceholder) to respect the cooldown and keep
addMessage/removeLastMessage behavior consistent while preventing multiple
concurrent sends by guarding submitEmail against re-entry.

10-10: ⚡ Quick win

ResponseValue type includes () => void, but void return is not explicitly handled.

Line 10 declares type ResponseValue = string | (() => string) | (() => void);, yet lines 127–133 only check for '__EMAIL_FLOW__' or typeof result === 'string'. If a function returns undefined (matching () => void), it silently falls through to the else branch at line 136, triggering the "didn't understand" fallback. Either remove () => void from the type or add explicit handling.

♻️ Proposed fix: remove unused `() => void` alternative
-type ResponseValue = string | (() => string) | (() => void);
+type ResponseValue = string | (() => string);

If you intend to support side-effect-only responses in the future, add a comment or explicit branch.

Also applies to: 127-138

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` at line 10, The ResponseValue union
currently includes an unused (() => void) variant which allows handler functions
to return undefined and then fall through to the "didn't understand" fallback;
remove (() => void) from the ResponseValue type declaration so ResponseValue is
only string | (() => string), and confirm any call-sites that invoke response
functions (the logic that checks for '__EMAIL_FLOW__' and uses typeof result ===
'string') still match the new type; if you intend to support side-effect-only
handlers later, instead keep the union but add an explicit branch that treats
undefined results as a valid no-op response (with a clear comment) so the
fallback is not triggered inadvertently.

152-154: 💤 Low value

Verify dialog semantics: role="dialog" with aria-modal="false" is uncommon.

Typically, role="dialog" implies a modal that requires user interaction before returning to the main content. Since this chatbot is non-blocking and the page remains interactive, consider whether role="complementary" or role="region" with aria-label is more semantically accurate, or confirm that aria-modal="false" is intentional for a non-modal dialog pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` around lines 152 - 154, The element in
Chatbot (component Chatbot, attributes role="dialog" and aria-modal="false")
uses dialog semantics but is non-modal; update the role to a more accurate
landmark (e.g., role="complementary" or role="region") and remove aria-modal, or
explicitly document/justify keeping role="dialog" and set aria-modal="true" if
it truly should be modal. Locate the JSX in Chatbot where role="dialog" and
aria-modal="false" are set and replace the role and remove aria-modal (or set
aria-modal to true) so semantics match the interactive behavior.
src/app/layout.tsx (1)

35-35: 💤 Low value

Consider future provider access when extending Chatbot.

The <Chatbot /> is rendered outside the MuiProvider and QueryProvider wrappers (line 35). For the current v0.2 implementation this is fine, but if you later add React Query hooks for backend integration or MUI components, you'll need to move it inside the providers or duplicate provider setup.

For now, this placement works. Just be aware of the scoping when planning backend integration or UI library usage in future versions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/layout.tsx` at line 35, Chatbot is rendered outside the providers
which will block future use of React Query or MUI inside it; move the <Chatbot
/> element so it is nested inside the existing MuiProvider and QueryProvider (or
duplicate the providers around Chatbot) so that any future additions using
useQuery/useMutation or MUI components in the Chatbot component have access to
those contexts; update the JSX where Chatbot is mounted to be enclosed by the
MuiProvider and QueryProvider components (references: Chatbot, MuiProvider,
QueryProvider).
src/components/Chatbot/Chatbot.css (1)

77-79: 💤 Low value

Remove empty line before transform-origin to satisfy linter.

Stylelint reports an empty line violation before the transform-origin declaration (line 78). While minor, this creates linter noise.

🎨 Proposed fix
  font-family: var(--font-geist-sans, sans-serif);
  color: `#fafafa`;
-
  transform-origin: bottom right;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.css` around lines 77 - 79, Remove the blank
line immediately before the transform-origin declaration so the rule reads
consecutively with transform-origin: bottom right; followed by transform:
scale(0.85) translateY(16px);; edit the block containing the transform-origin
and transform declarations (the transform-origin declaration and the transform:
scale(...) line) to eliminate the empty line to satisfy Stylelint.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Chatbot/Chatbot.css`:
- Line 169: In src/components/Chatbot/Chatbot.css replace the deprecated CSS
declaration "word-break: break-word" with the modern equivalent "overflow-wrap:
break-word" in the rule that currently contains that property; locate the
occurrence of the "word-break: break-word" declaration and remove or replace it
with "overflow-wrap: break-word" so the element keeps the same wrapping behavior
without using the deprecated property.

In `@src/components/Chatbot/Chatbot.tsx`:
- Line 137: The fallback message shown via addMessage("Sorry, I didn't
understand that. Try: hello, today, time, ping, rn."); omits the 'about' and
'hey' commands defined in responseObj; update the fallback text in the Chatbot
component to include all keys from responseObj (e.g., hello, hey, about, today,
time, ping, rn) or generate the list dynamically from Object.keys(responseObj)
so the message always matches available commands.
- Around line 93-98: The EmailJS service/template/public key values are
hardcoded in the Chatbot component (the emailjs.send call) which prevents
rotation and exposes credentials; replace the literal strings 'service_2fc5v5k',
'template_d3d70zp', and 'De3J97q5iJ8M72LB0' with environment variables (e.g.
process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID,
process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID,
process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY), add those vars to .env.local as
suggested, and add a runtime check in the Chatbot component (before calling
emailjs.send) to log/throw a clear error if any of the required env vars are
missing so the app fails fast and credentials can be rotated without code
changes.
- Around line 33-34: The module-level counter (msgId) and generator (nextId) can
collide under React 19 Strict Mode and across multiple Chatbot instances; move
the counter into the Chatbot component scope using a persistent per-instance ref
or state (e.g. useRef in the Chatbot component) and replace usages of
nextId()/msgId with the per-instance ref (increment and read ref.current) so IDs
are stable per mount and not incremented globally across double-mounts or
multiple instances; update any references to nextId() to use the new
per-instance generator logic inside Chatbot.

---

Nitpick comments:
In `@src/app/layout.tsx`:
- Line 35: Chatbot is rendered outside the providers which will block future use
of React Query or MUI inside it; move the <Chatbot /> element so it is nested
inside the existing MuiProvider and QueryProvider (or duplicate the providers
around Chatbot) so that any future additions using useQuery/useMutation or MUI
components in the Chatbot component have access to those contexts; update the
JSX where Chatbot is mounted to be enclosed by the MuiProvider and QueryProvider
components (references: Chatbot, MuiProvider, QueryProvider).

In `@src/components/Chatbot/Chatbot.css`:
- Around line 77-79: Remove the blank line immediately before the
transform-origin declaration so the rule reads consecutively with
transform-origin: bottom right; followed by transform: scale(0.85)
translateY(16px);; edit the block containing the transform-origin and transform
declarations (the transform-origin declaration and the transform: scale(...)
line) to eliminate the empty line to satisfy Stylelint.

In `@src/components/Chatbot/Chatbot.tsx`:
- Line 58: Replace the three magic-number timeouts used in setTimeout calls
(e.g., the call that invokes inputRef.current?.focus(), the timeout used around
EMAIL focus, and the BOT response delay) with descriptive named constants
defined at the top of the Chatbot component file (for example FOCUS_DELAY_MS,
EMAIL_FOCUS_DELAY_MS, BOT_RESPONSE_DELAY_MS) and add brief comments explaining
each constant's purpose (e.g., "wait for open animation", "small email input
focus latency", "simulate bot typing delay"); then update the setTimeout
invocations to use these constants so the intent is documented and maintainable.
- Around line 82-84: The email validation branch currently shows an error
message but leaves the malformed value in the input; in the handler that checks
result.success (likely the email submit handler in Chatbot component, e.g.,
handleEmailSubmit or the function where result and addMessage are used) after
calling addMessage('Invalid email. Please try again.') also clear the input
state (call the component's input state setter such as setInput('') or reset the
controlled input value) and ensure any related validation state is reset so the
UI empties the field on validation failure.
- Around line 80-109: The submitEmail flow can be abused by repeated
submissions; add a client-side cooldown state (e.g., emailCooldown boolean or
emailCooldownUntil timestamp) checked at the start of submitEmail to
short-circuit and show a message if still cooling down, set that state when a
send succeeds (or on attempt) and use setTimeout or calculating remaining time
to clear it after ~60s, and ensure the UI/send button is disabled using that
state; update places that toggle email mode (setIsEmailMode, setPlaceholder) to
respect the cooldown and keep addMessage/removeLastMessage behavior consistent
while preventing multiple concurrent sends by guarding submitEmail against
re-entry.
- Line 10: The ResponseValue union currently includes an unused (() => void)
variant which allows handler functions to return undefined and then fall through
to the "didn't understand" fallback; remove (() => void) from the ResponseValue
type declaration so ResponseValue is only string | (() => string), and confirm
any call-sites that invoke response functions (the logic that checks for
'__EMAIL_FLOW__' and uses typeof result === 'string') still match the new type;
if you intend to support side-effect-only handlers later, instead keep the union
but add an explicit branch that treats undefined results as a valid no-op
response (with a clear comment) so the fallback is not triggered inadvertently.
- Around line 152-154: The element in Chatbot (component Chatbot, attributes
role="dialog" and aria-modal="false") uses dialog semantics but is non-modal;
update the role to a more accurate landmark (e.g., role="complementary" or
role="region") and remove aria-modal, or explicitly document/justify keeping
role="dialog" and set aria-modal="true" if it truly should be modal. Locate the
JSX in Chatbot where role="dialog" and aria-modal="false" are set and replace
the role and remove aria-modal (or set aria-modal to true) so semantics match
the interactive behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b11b9d52-776c-4831-b5f1-6bfddb2dbdbe

📥 Commits

Reviewing files that changed from the base of the PR and between a46c5d5 and db37920.

⛔ Files ignored due to path filters (2)
  • public/images/logo.png is excluded by !**/*.png
  • public/images/send.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • src/app/layout.tsx
  • src/components/Chatbot/Chatbot.css
  • src/components/Chatbot/Chatbot.tsx

max-width: 82%;
font-size: 0.875rem;
line-height: 1.45;
word-break: break-word;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace deprecated word-break: break-word with modern property.

word-break: break-word is deprecated. Use overflow-wrap: break-word instead for the same behavior without the deprecation warning.

🔧 Proposed fix
  max-width: 82%;
  font-size: 0.875rem;
  line-height: 1.45;
- word-break: break-word;
+ overflow-wrap: break-word;
}
📝 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.

Suggested change
word-break: break-word;
max-width: 82%;
font-size: 0.875rem;
line-height: 1.45;
overflow-wrap: break-word;
}
🧰 Tools
🪛 Stylelint (17.11.1)

[error] 169-169: Deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)

(declaration-property-value-keyword-no-deprecated)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.css` at line 169, In
src/components/Chatbot/Chatbot.css replace the deprecated CSS declaration
"word-break: break-word" with the modern equivalent "overflow-wrap: break-word"
in the rule that currently contains that property; locate the occurrence of the
"word-break: break-word" declaration and remove or replace it with
"overflow-wrap: break-word" so the element keeps the same wrapping behavior
without using the deprecated property.

Comment on lines +33 to +34
let msgId = 0;
const nextId = () => ++msgId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Global message ID counter may cause collisions in React 19 Strict Mode.

React 19 Strict Mode double-mounts components in development, and this module-level counter will increment across both mount cycles, potentially skipping IDs or causing inconsistencies if multiple Chatbot instances ever exist.

♻️ Proposed fix: move counter into component state
-let msgId = 0;
-const nextId = () => ++msgId;

 export default function Chatbot() {
+  const nextIdRef = useRef(0);
+  const nextId = () => ++nextIdRef.current;
+
   const [isOpen, setIsOpen] = useState(false);
📝 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.

Suggested change
let msgId = 0;
const nextId = () => ++msgId;
export default function Chatbot() {
const nextIdRef = useRef(0);
const nextId = () => ++nextIdRef.current;
const [isOpen, setIsOpen] = useState(false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` around lines 33 - 34, The module-level
counter (msgId) and generator (nextId) can collide under React 19 Strict Mode
and across multiple Chatbot instances; move the counter into the Chatbot
component scope using a persistent per-instance ref or state (e.g. useRef in the
Chatbot component) and replace usages of nextId()/msgId with the per-instance
ref (increment and read ref.current) so IDs are stable per mount and not
incremented globally across double-mounts or multiple instances; update any
references to nextId() to use the new per-instance generator logic inside
Chatbot.

} else if (typeof res === 'string') {
addMessage(res);
} else {
addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping, rn.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fallback message does not list all available commands.

The fallback mentions hello, today, time, ping, rn but omits about and hey, which are also defined in responseObj (lines 13, 19). This may confuse users who see incomplete help.

📝 Proposed fix
      } else {
-       addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping, rn.");
+       addMessage("Sorry, I didn't understand that. Try: hello, hey, today, time, ping, about, rn.");
      }
📝 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.

Suggested change
addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping, rn.");
addMessage("Sorry, I didn't understand that. Try: hello, hey, today, time, ping, about, rn.");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` at line 137, The fallback message shown
via addMessage("Sorry, I didn't understand that. Try: hello, today, time, ping,
rn."); omits the 'about' and 'hey' commands defined in responseObj; update the
fallback text in the Chatbot component to include all keys from responseObj
(e.g., hello, hey, about, today, time, ping, rn) or generate the list
dynamically from Object.keys(responseObj) so the message always matches
available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Chatbot/Chatbot.tsx`:
- Around line 93-98: Replace the hardcoded EmailJS placeholders in Chatbot.tsx
by reading service ID, template ID and public API key from environment variables
(e.g., process.env.EMAILJS_SERVICE_ID, process.env.EMAILJS_TEMPLATE_ID,
process.env.EMAILJS_PUBLIC_KEY) and validate they exist before calling
emailjs.send (return or show error if missing); update the emailjs.send call in
the Chatbot component to pass those env values instead of
'YOUR_SERVICE'/'YOUR_TEMPLATE'/'YOUR_APIKEY'. Also add `@emailjs/browser` to
package.json dependencies (use a v4-compatible version that supports the
fourth-argument { publicKey }) and run npm/yarn install so the import of emailjs
in Chatbot.tsx resolves at build/runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d76275e-6c1e-4738-be0c-0c83edfa7f05

📥 Commits

Reviewing files that changed from the base of the PR and between db37920 and ec40e9e.

📒 Files selected for processing (1)
  • src/components/Chatbot/Chatbot.tsx

Comment on lines +93 to +98
await emailjs.send(
'YOUR_SERVICE',
'YOUR_TEMPLATE',
{ user_email: email, user: 'IssueHub' },
{ publicKey: 'YOUR_APIKEY' }
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check that `@emailjs/browser` is declared and inspect the pinned version range
fd -t f 'package.json' -E 'node_modules' --exec sh -c 'echo "== {} =="; jq ".dependencies[\"`@emailjs/browser`\"] // .devDependencies[\"`@emailjs/browser`\"]" "{}"'

Repository: kanywst/IssueHub

Length of output: 86


🌐 Web query:

Does @emailjs/browser v4 send() accept an options object with publicKey as the 4th argument, and how does this differ from v3?

💡 Result:

Yes. In @emailjs/browser v4, emailjs.send(serviceId, templateId, templateParams, options) accepts an options object as the 4th argument, and that options object can include { publicKey: 'YOUR_PUBLIC_KEY' } [1][2]. The docs also state that options can be declared globally via emailjs.init(...) or locally as the fourth parameter, with the local parameter having higher priority [1]. How this differs from v3: I could not find v3-specific, official API documentation in the sources I retrieved that clearly states the v3 send signature or whether v3’s 4th argument is an options object. However, v3 client usage widely referenced in community material indicates supplying the public key as a positional argument to send (i.e., not an options object) [3]. Example (v4): emailjs.send('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', templateParams, { publicKey: 'YOUR_PUBLIC_KEY' }) [1][2].

Citations:


Fix EmailJS subscription send: hardcoded placeholders + undeclared dependency

  • In src/components/Chatbot/Chatbot.tsx (lines 93-98), 'YOUR_SERVICE', 'YOUR_TEMPLATE', and publicKey: 'YOUR_APIKEY' are non-functional placeholders; load these from environment variables and guard for missing values before calling emailjs.send.
  • @emailjs/browser is imported but not declared in package.json, so the module may fail to resolve at build/runtime. Add @emailjs/browser to dependencies and ensure the installed version supports the current v4-style { publicKey } 4th-argument options.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Chatbot/Chatbot.tsx` around lines 93 - 98, Replace the
hardcoded EmailJS placeholders in Chatbot.tsx by reading service ID, template ID
and public API key from environment variables (e.g.,
process.env.EMAILJS_SERVICE_ID, process.env.EMAILJS_TEMPLATE_ID,
process.env.EMAILJS_PUBLIC_KEY) and validate they exist before calling
emailjs.send (return or show error if missing); update the emailjs.send call in
the Chatbot component to pass those env values instead of
'YOUR_SERVICE'/'YOUR_TEMPLATE'/'YOUR_APIKEY'. Also add `@emailjs/browser` to
package.json dependencies (use a v4-compatible version that supports the
fourth-argument { publicKey }) and run npm/yarn install so the import of emailjs
in Chatbot.tsx resolves at build/runtime.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant