Refactor chat form and AI greeting with flexible partials

- Extract message form to a reusable partial with dynamic context support
- Create flexible AI greeting partial for consistent welcome messages
- Simplify chat and sidebar views by leveraging new partials
- Add support for different form scenarios (chat, new chat, sidebar)
- Improve code modularity and reduce duplication
This commit is contained in:
Josh Pigford
2025-02-27 17:36:48 -05:00
parent c0a3ff86a2
commit dbde017128
4 changed files with 85 additions and 173 deletions

View File

@@ -26,49 +26,12 @@
<% end %>
<% else %>
<!-- Show welcome message when chat has no messages -->
<div class="flex items-start gap-3 mb-4 w-full">
<%= render "layouts/shared/ai_avatar" %>
<div class="p-4 max-w-[85%] text-gray-800">
<p>Hey <%= Current.user&.first_name || 'there' %>! I'm an AI built by Maybe to help with your finances. How can I assist you today?</p>
</div>
</div>
<%= render "layouts/shared/ai_greeting", context: 'chat' %>
<% end %>
</div>
</div>
<div class="border-t border-gray-200 p-4">
<%= form_with model: [@chat, @message], class: "relative", data: { controller: "message-form", action: "turbo:submit-end->message-form#reset turbo:submit-end->chat-scroll#scrollToBottom" } do |f| %>
<div class="bg-white border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2">
<%= f.text_area :content,
placeholder: "Ask anything...",
class: "w-full border-0 focus:ring-0 resize-none text-sm",
rows: 1,
data: {
controller: "textarea-autogrow",
action: "input->textarea-autogrow#resize keydown->message-form#checkSubmit"
} %>
</div>
<div class="flex items-center px-2 py-2 border-t border-gray-200">
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("plus") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("command") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("at-sign") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("sparkles") %>
</button>
<button type="submit" class="ml-auto p-1.5 text-gray-500 hover:text-gray-700 rounded-md" data-message-form-target="submit">
<%= icon("arrow-up") %>
</button>
</div>
</div>
<% end %>
<%= render "messages/form", chat: @chat, message: @message, scroll_behavior: true %>
</div>
</div>

View File

@@ -0,0 +1,41 @@
<%#
This partial renders the AI greeting message.
Parameters:
- context: Either 'chat' (for existing chat) or 'default' (for new chat)
%>
<% context ||= 'default' %>
<div class="flex items-start gap-1 mt-4 w-full">
<%= render "layouts/shared/ai_avatar" %>
<div class="pt-2 pr-1 max-w-[85%] text-gray-800 text-sm">
<% if context == 'chat' %>
<p>Hey <%= Current.user&.first_name || 'there' %>! I'm an AI built by Maybe to help with your finances. How can I assist you today?</p>
<% else %>
<p>Hey <%= Current.user&.first_name || 'there' %>! I'm an AI built by Maybe to help with your finances. I have access to the web and your account data.</p>
<div class="mt-4 text-gray-600">
You can use <span class="bg-white border border-gray-200 px-1.5 py-0.5 rounded font-mono text-xs">/</span> to access commands
</div>
<div class="mt-4">
<p class="text-gray-600 mb-3">Here's a few questions you can ask:</p>
<div class="space-y-2">
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("bar-chart-2") %> Evaluate investment portfolio
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("credit-card") %> Show spending insights
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("alert-triangle") %> Find unusual patterns
</button>
</div>
</div>
<% end %>
</div>
</div>

View File

@@ -101,74 +101,11 @@
</div>
<% end %>
<% else %>
<div class="flex items-start gap-1 mt-4 w-full">
<%= render "layouts/shared/ai_avatar" %>
<div class="pr-1 max-w-[85%] text-gray-800">
<p>Hey <%= Current.user&.first_name || 'there' %>! I'm an AI built by Maybe to help with your finances. How can I assist you today?</p>
</div>
</div>
<%= render "layouts/shared/ai_greeting", context: 'chat' %>
<% end %>
</div>
<% else %>
<% if Current.user %>
<div class="flex items-start gap-1 mt-4 w-full">
<%= render "layouts/shared/ai_avatar" %>
<div class="pr-1 max-w-[85%] text-gray-800">
<p>Hey <%= Current.user&.first_name || 'there' %>! I'm an AI built by Maybe to help with your finances. I have access to the web and your account data.</p>
<div class="mt-4 text-gray-600">
You can use <span class="bg-gray-200 px-2 py-1 rounded">/ </span> to access commands
</div>
<div class="mt-4">
<p class="text-gray-600 mb-3">Here's a few questions you can ask:</p>
<div class="space-y-2">
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("bar-chart-2") %> Evaluate investment portfolio
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("credit-card") %> Show spending insights
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("alert-triangle") %> Find unusual patterns
</button>
</div>
</div>
</div>
</div>
<% else %>
<div class="flex items-start gap-1 mt-4 w-full">
<%= render "layouts/shared/ai_avatar" %>
<div class="p-4 max-w-[85%] text-gray-800">
<p>Hey there! I'm an AI built by Maybe to help with your finances. I have access to the web and your account data.</p>
<div class="mt-4 text-gray-600">
You can use <span class="bg-gray-200 px-2 py-1 rounded">/ </span> to access commands
</div>
<div class="mt-4">
<p class="text-gray-600 mb-3">Here's a few questions you can ask:</p>
<div class="space-y-2">
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("bar-chart-2") %> Evaluate investment portfolio
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("credit-card") %> Show spending insights
</button>
<button class="w-full flex items-center gap-2 bg-white border border-gray-200 rounded-full py-2 px-4 text-left hover:bg-gray-50">
<%= icon("alert-triangle") %> Find unusual patterns
</button>
</div>
</div>
</div>
</div>
<% end %>
<%= render "layouts/shared/ai_greeting", context: 'default' %>
<% end %>
</div>
@@ -187,73 +124,9 @@
<div class="px-4 py-3">
<% if Current.user && @chat.present? %>
<%= form_with model: [@chat, @message], class: "relative", data: { controller: "message-form", action: "turbo:submit-end->message-form#reset turbo:submit-end->chat-scroll#scrollToBottom" } do |f| %>
<div class="bg-white border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2">
<%= f.text_area :content,
placeholder: "Ask anything ...",
class: "w-full border-0 focus:ring-0 resize-none text-sm",
rows: 1,
data: {
controller: "textarea-autogrow",
action: "input->textarea-autogrow#resize keydown->message-form#checkSubmit"
} %>
</div>
<div class="flex items-center px-2 py-2 border-t border-gray-200">
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("plus") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("command") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("at-sign") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("sparkles") %>
</button>
<button type="submit" class="ml-auto p-1.5 text-gray-500 hover:text-gray-700 rounded-md" data-message-form-target="submit">
<%= icon("arrow-up") %>
</button>
</div>
</div>
<% end %>
<%= render "messages/form", chat: @chat, message: @message, scroll_behavior: true %>
<% else %>
<%= form_with url: chats_path, method: :post, class: "relative", data: { controller: "message-form", action: "turbo:submit-end->message-form#reset turbo:submit-end->chat-scroll#scrollToBottom" } do |f| %>
<div class="bg-white border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2">
<%= f.text_area :content,
placeholder: "Ask anything ...",
class: "w-full border-0 focus:ring-0 resize-none text-sm",
rows: 1,
data: {
controller: "textarea-autogrow",
action: "input->textarea-autogrow#resize keydown->message-form#checkSubmit"
} %>
</div>
<div class="flex items-center px-2 py-2 border-t border-gray-200">
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("plus") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("command") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("at-sign") %>
</button>
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("sparkles") %>
</button>
<button type="submit" class="ml-auto p-1.5 text-gray-500 hover:text-gray-700 rounded-md" data-message-form-target="submit">
<%= icon("arrow-up") %>
</button>
</div>
</div>
<% end %>
<%= render "messages/form", scroll_behavior: true %>
<% end %>
<p class="text-xs text-gray-500 text-center mt-2">AI may make mistakes. Make sure to double check responses.</p>
</div>

View File

@@ -1,6 +1,41 @@
<%= form_with model: [chat, message], class: "relative", data: { controller: "message-form", action: "turbo:submit-end->message-form#reset" } do |f| %>
<%
# This partial can be used in multiple contexts:
# - In a chat show page: form_with model: [chat, message]
# - In a new chat context: form_with url: chats_path, method: :post
# - In a sidebar: with additional scroll behavior
#
# Parameters:
# - chat: The chat object (optional, if creating a new chat)
# - message: The message object (optional, if creating a new chat)
# - form_options: Additional options for the form (default: {})
# - scroll_behavior: Whether to include scroll behavior (default: false)
%>
<%
# Set up default values
form_options ||= {}
scroll_behavior ||= false
# Determine form parameters based on context
if defined?(chat) && chat.present? && defined?(message) && message.present?
form_params = { model: [chat, message] }
else
form_params = { url: chats_path, method: :post }
end
# Merge form options
form_params.merge!(form_options)
# Set up controller and actions
controller_data = { controller: "message-form", action: "turbo:submit-end->message-form#reset" }
if scroll_behavior
controller_data[:action] += " turbo:submit-end->chat-scroll#scrollToBottom"
end
%>
<%= form_with **form_params, class: "relative", data: controller_data do |f| %>
<div class="bg-white border border-gray-300 rounded-lg overflow-hidden">
<div class="px-3 py-2">
<div class="px-3 pt-2">
<%= f.text_area :content,
placeholder: "Ask anything...",
class: "w-full border-0 focus:ring-0 resize-none text-sm",
@@ -11,7 +46,7 @@
} %>
</div>
<div class="flex items-center px-2 py-2 border-t border-gray-200">
<div class="flex items-center px-2 pb-2">
<button type="button" class="p-1.5 text-gray-500 hover:text-gray-700 rounded-md">
<%= icon("plus") %>
</button>