1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-07 22:45:20 +02:00

AI chat layout skeleton

This commit is contained in:
Zach Gollwitzer 2025-03-12 12:39:16 -04:00
parent dd75cadebc
commit d1b83541c1
23 changed files with 231 additions and 1 deletions

View file

@ -0,0 +1,17 @@
class ChatsController < ApplicationController
def index
Current.user.update!(current_chat: nil)
@chats = Current.user.chats.ordered
end
def create
@chat = Current.user.chats.create_with_defaults!
redirect_to chat_path(@chat)
end
def show
@chat = Current.user.chats.find(params[:id])
Current.user.update!(current_chat: @chat)
end
end

View file

@ -0,0 +1,18 @@
class MessagesController < ApplicationController
before_action :set_chat
def create
@message = @chat.messages.create!(message_params.merge(role: "user"))
redirect_to chat_path(@chat)
end
private
def set_chat
@chat = Current.user.chats.find(params[:chat_id])
end
def message_params
params.require(:message).permit(:content)
end
end

23
app/models/chat.rb Normal file
View file

@ -0,0 +1,23 @@
class Chat < ApplicationRecord
belongs_to :user
has_many :messages, dependent: :destroy
has_one :user_current_chat, class_name: "User", foreign_key: :current_chat_id, dependent: :nullify
validates :title, presence: true
scope :ordered, -> { order(created_at: :desc) }
class << self
def create_with_defaults!
create!(
title: "New chat #{Time.current.strftime("%Y-%m-%d %H:%M:%S")}",
messages: [
Message.new(
role: "system",
content: "You are a helpful personal finance assistant.",
)
]
)
end
end
end

10
app/models/message.rb Normal file
View file

@ -0,0 +1,10 @@
class Message < ApplicationRecord
belongs_to :chat
enum :role, { user: "user", assistant: "assistant", system: "system" }
validates :content, presence: true
validates :role, presence: true
scope :conversation, -> { where(debug_mode: false, role: [ :user, :assistant ]) }
end

View file

@ -1,10 +1,12 @@
class User < ApplicationRecord
has_secure_password
belongs_to :current_chat, class_name: "Chat", optional: true
belongs_to :family
has_many :sessions, dependent: :destroy
has_many :impersonator_support_sessions, class_name: "ImpersonationSession", foreign_key: :impersonator_id, dependent: :destroy
has_many :impersonated_support_sessions, class_name: "ImpersonationSession", foreign_key: :impersonated_id, dependent: :destroy
has_many :chats, dependent: :destroy
accepts_nested_attributes_for :family, update_only: true
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }

View file

@ -0,0 +1,3 @@
<%= link_to chat_path(chat) do %>
<%= chat.title %>
<% end %>

View file

@ -0,0 +1,6 @@
<div>
<p>Chat nav</p>
<%= button_to "New chat", chats_path, method: :post, class: "btn btn-primary", data: { turbo_frame: "chat_content" } %>
<%= link_to "All chats", chats_path, data: { turbo_frame: "chat_content" } %>
</div>

View file

@ -0,0 +1,9 @@
<%= turbo_frame_tag "chat_content" do %>
<% if @chats.empty? %>
<p>No chats</p>
<% else %>
<div class="flex flex-col gap-2">
<%= render @chats %>
</div>
<% end %>
<% end %>

View file

@ -0,0 +1,13 @@
<%= turbo_frame_tag "chat_content" do %>
<div class="flex flex-col gap-4 justify-between h-full">
<div>
<h2 class="text-2xl font-bold mb-6"><%= @chat.title %></h2>
<div class="space-y-6">
<%= render @chat.messages.conversation %>
</div>
</div>
<%= render "messages/form", chat: @chat %>
</div>
<% end %>

View file

@ -57,5 +57,23 @@
<%= yield %>
<% end %>
<% end %>
<%= tag.div class: class_names("py-4 shrink-0 flex flex-col justify-between gap-6 h-full overflow-y-auto transition-all duration-300 w-[375px]") do %>
<div class="bg-white p-4">
<%= render "chats/chat_nav" %>
</div>
<div id="chat-content" class="grow p-4 bg-white" data-turbo-permanent>
<% if Current.user.current_chat.present? %>
<%= turbo_frame_tag "chat_content", src: chat_path(Current.user.current_chat), loading: "lazy" do %>
<p>Loading chat...</p>
<% end %>
<% else %>
<%= turbo_frame_tag "chat_content", src: chats_path, loading: "lazy" do %>
<p>Loading chats...</p>
<% end %>
<% end %>
</div>
<% end %>
</div>
<% end %>

View file

@ -0,0 +1,6 @@
<%# locals: (chat:) %>
<%= styled_form_with model: chat.messages.build, url: chat_messages_path(chat), method: :post, class: "space-y-4" do |f| %>
<%= f.text_area :content, size: "50x5" %>
<%= f.submit "Send message" %>
<% end %>

View file

@ -0,0 +1,8 @@
<div>
<p class="text-sm text-gray-500">
role: <%= message.role %>
</p>
<p class="text-gray-900">
<%= message.content %>
</p>
</div>