diff --git a/Gemfile.lock b/Gemfile.lock index 298d8e041..74cdbeea8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -33,70 +33,70 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + actioncable (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailbox (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.5) - actionpack (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionmailer (7.0.8.7) + actionpack (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activesupport (= 7.0.8.7) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.8.5) - actionview (= 7.0.8.5) - activesupport (= 7.0.8.5) + actionpack (7.0.8.7) + actionview (= 7.0.8.7) + activesupport (= 7.0.8.7) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.5) - actionpack (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + actiontext (7.0.8.7) + actionpack (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.5) - activesupport (= 7.0.8.5) + actionview (7.0.8.7) + activesupport (= 7.0.8.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_record_query_trace (1.8) - activejob (7.0.8.5) - activesupport (= 7.0.8.5) + activejob (7.0.8.7) + activesupport (= 7.0.8.7) globalid (>= 0.3.6) - activemodel (7.0.8.5) - activesupport (= 7.0.8.5) - activerecord (7.0.8.5) - activemodel (= 7.0.8.5) - activesupport (= 7.0.8.5) + activemodel (7.0.8.7) + activesupport (= 7.0.8.7) + activerecord (7.0.8.7) + activemodel (= 7.0.8.7) + activesupport (= 7.0.8.7) activerecord-import (1.4.1) activerecord (>= 4.2) - activestorage (7.0.8.5) - actionpack (= 7.0.8.5) - activejob (= 7.0.8.5) - activerecord (= 7.0.8.5) - activesupport (= 7.0.8.5) + activestorage (7.0.8.7) + actionpack (= 7.0.8.7) + activejob (= 7.0.8.7) + activerecord (= 7.0.8.7) + activesupport (= 7.0.8.7) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.8.5) + activesupport (7.0.8.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -472,7 +472,7 @@ GEM mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.8) - minitest (5.25.1) + minitest (5.25.4) mock_redis (0.36.0) ruby2_keywords msgpack (1.7.0) @@ -502,14 +502,14 @@ GEM newrelic_rpm (9.6.0) base64 nio4r (2.7.3) - nokogiri (1.16.8) + nokogiri (1.17.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.16.8-arm64-darwin) + nokogiri (1.17.1-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.8-x86_64-darwin) + nokogiri (1.17.1-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.8-x86_64-linux) + nokogiri (1.17.1-x86_64-linux) racc (~> 1.4) oauth (1.1.0) oauth-tty (~> 1.0, >= 1.0.1) @@ -581,20 +581,20 @@ GEM rack-test (2.1.0) rack (>= 1.3) rack-timeout (0.6.3) - rails (7.0.8.5) - actioncable (= 7.0.8.5) - actionmailbox (= 7.0.8.5) - actionmailer (= 7.0.8.5) - actionpack (= 7.0.8.5) - actiontext (= 7.0.8.5) - actionview (= 7.0.8.5) - activejob (= 7.0.8.5) - activemodel (= 7.0.8.5) - activerecord (= 7.0.8.5) - activestorage (= 7.0.8.5) - activesupport (= 7.0.8.5) + rails (7.0.8.7) + actioncable (= 7.0.8.7) + actionmailbox (= 7.0.8.7) + actionmailer (= 7.0.8.7) + actionpack (= 7.0.8.7) + actiontext (= 7.0.8.7) + actionview (= 7.0.8.7) + activejob (= 7.0.8.7) + activemodel (= 7.0.8.7) + activerecord (= 7.0.8.7) + activestorage (= 7.0.8.7) + activesupport (= 7.0.8.7) bundler (>= 1.15.0) - railties (= 7.0.8.5) + railties (= 7.0.8.7) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -602,9 +602,9 @@ GEM rails-html-sanitizer (1.6.1) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.0.8.5) - actionpack (= 7.0.8.5) - activesupport (= 7.0.8.5) + railties (7.0.8.7) + actionpack (= 7.0.8.7) + activesupport (= 7.0.8.7) method_source rake (>= 12.2) thor (~> 1.0) diff --git a/app/controllers/api/v1/accounts/contacts_controller.rb b/app/controllers/api/v1/accounts/contacts_controller.rb index 250c64a86..0d8e0ed93 100644 --- a/app/controllers/api/v1/accounts/contacts_controller.rb +++ b/app/controllers/api/v1/accounts/contacts_controller.rb @@ -14,7 +14,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController before_action :check_authorization before_action :set_current_page, only: [:index, :active, :search, :filter] before_action :fetch_contact, only: [:show, :update, :destroy, :avatar, :contactable_inboxes, :destroy_custom_attributes] - before_action :set_include_contact_inboxes, only: [:index, :search, :filter] + before_action :set_include_contact_inboxes, only: [:index, :search, :filter, :show, :update] def index @contacts_count = resolved_contacts.count diff --git a/app/controllers/api/v1/accounts/integrations/captain_controller.rb b/app/controllers/api/v1/accounts/integrations/captain_controller.rb index 15e81b726..6813d14b5 100644 --- a/app/controllers/api/v1/accounts/integrations/captain_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/captain_controller.rb @@ -2,10 +2,22 @@ class Api::V1::Accounts::Integrations::CaptainController < Api::V1::Accounts::Ba before_action :hook def proxy + request_url = build_request_url(request_path) response = HTTParty.send(request_method, request_url, body: permitted_params[:body].to_json, headers: headers) render plain: response.body, status: response.code end + def copilot + request_url = build_request_url(build_request_path("/assistants/#{hook.settings['assistant_id']}/copilot")) + params = { + previous_messages: copilot_params[:previous_messages], + conversation_history: conversation_history, + message: copilot_params[:message] + } + response = HTTParty.send(:post, request_url, body: params.to_json, headers: headers) + render plain: response.body, status: response.code + end + private def headers @@ -17,15 +29,19 @@ class Api::V1::Accounts::Integrations::CaptainController < Api::V1::Accounts::Ba } end + def build_request_path(route) + "api/accounts/#{hook.settings['account_id']}#{route}" + end + def request_path request_route = with_leading_hash_on_route(params[:route]) return 'api/sessions/profile' if request_route == '/sessions/profile' - "api/accounts/#{hook.settings['account_id']}#{request_route}" + build_request_path(request_route) end - def request_url + def build_request_url(request_path) base_url = InstallationConfig.find_by(name: 'CAPTAIN_API_URL').value URI.join(base_url, request_path).to_s end @@ -47,6 +63,15 @@ class Api::V1::Accounts::Integrations::CaptainController < Api::V1::Accounts::Ba request_route.start_with?('/') ? request_route : "/#{request_route}" end + def conversation_history + conversation = Current.account.conversations.find_by!(display_id: copilot_params[:conversation_id]) + conversation.to_llm_text + end + + def copilot_params + params.permit(:previous_messages, :conversation_id, :message) + end + def permitted_params params.permit(:method, :route, body: {}) end diff --git a/app/javascript/dashboard/api/contacts.js b/app/javascript/dashboard/api/contacts.js index 85c4bba1d..2eee3f484 100644 --- a/app/javascript/dashboard/api/contacts.js +++ b/app/javascript/dashboard/api/contacts.js @@ -27,6 +27,14 @@ class ContactAPI extends ApiClient { return axios.get(requestURL); } + show(id) { + return axios.get(`${this.url}/${id}?include_contact_inboxes=false`); + } + + update(id, data) { + return axios.patch(`${this.url}/${id}?include_contact_inboxes=false`, data); + } + getConversations(contactId) { return axios.get(`${this.url}/${contactId}/conversations`); } diff --git a/app/javascript/dashboard/api/integrations.js b/app/javascript/dashboard/api/integrations.js index 59a3e244a..b78a2ea7e 100644 --- a/app/javascript/dashboard/api/integrations.js +++ b/app/javascript/dashboard/api/integrations.js @@ -36,6 +36,10 @@ class IntegrationsAPI extends ApiClient { requestCaptain(body) { return axios.post(`${this.baseUrl()}/integrations/captain/proxy`, body); } + + requestCaptainCopilot(body) { + return axios.post(`${this.baseUrl()}/integrations/captain/copilot`, body); + } } export default new IntegrationsAPI(); diff --git a/app/javascript/dashboard/assets/scss/_woot.scss b/app/javascript/dashboard/assets/scss/_woot.scss index dd9976fba..c6304cf19 100644 --- a/app/javascript/dashboard/assets/scss/_woot.scss +++ b/app/javascript/dashboard/assets/scss/_woot.scss @@ -72,6 +72,19 @@ --slate-11: 96 100 108; --slate-12: 28 32 36; + --iris-1: 253 253 255; + --iris-2: 248 248 255; + --iris-3: 240 241 254; + --iris-4: 230 231 255; + --iris-5: 218 220 255; + --iris-6: 203 205 255; + --iris-7: 184 186 248; + --iris-8: 155 158 240; + --iris-9: 91 91 214; + --iris-10: 81 81 205; + --iris-11: 87 83 198; + --iris-12: 39 41 98; + --ruby-1: 255 252 253; --ruby-2: 255 247 248; --ruby-3: 254 234 237; @@ -148,6 +161,19 @@ --slate-11: 176 180 186; --slate-12: 237 238 240; + --iris-1: 19 19 30; + --iris-2: 23 22 37; + --iris-3: 32 34 72; + --iris-4: 38 42 101; + --iris-5: 48 51 116; + --iris-6: 61 62 130; + --iris-7: 74 74 149; + --iris-8: 89 88 177; + --iris-9: 91 91 214; + --iris-10: 84 114 228; + --iris-11: 158 177 255; + --iris-12: 224 223 254; + --ruby-1: 25 17 19; --ruby-2: 30 21 23; --ruby-3: 58 20 30; diff --git a/app/javascript/dashboard/assets/scss/widgets/_tabs.scss b/app/javascript/dashboard/assets/scss/widgets/_tabs.scss index b9999fac9..01ccaf671 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_tabs.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_tabs.scss @@ -6,6 +6,14 @@ @apply border-b border-slate-50 dark:border-slate-800/50; } +.tabs--container--compact.tab--chat-type { + .tabs-title { + a { + @apply py-2 text-sm; + } + } +} + .tabs { @apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4 list-none mb-0; } diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue index 9875ae2ac..a3b0bf37c 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsCard/ContactsCard.vue @@ -6,6 +6,7 @@ import CardLayout from 'dashboard/components-next/CardLayout.vue'; import ContactsForm from 'dashboard/components-next/Contacts/ContactsForm/ContactsForm.vue'; import Button from 'dashboard/components-next/button/Button.vue'; import Avatar from 'dashboard/components-next/avatar/Avatar.vue'; +import Flag from 'dashboard/components-next/flag/Flag.vue'; import countries from 'shared/constants/countries'; const props = defineProps({ @@ -56,13 +57,19 @@ const countryDetails = computed(() => { if (!activeCountry) return null; - const parts = [ - activeCountry.emoji, - city ? `${city},` : null, - activeCountry.name, - ].filter(Boolean); + return { + countryCode: activeCountry.id, + city: city ? `${city},` : null, + name: activeCountry.name, + }; +}); - return parts.length ? parts.join(' ') : null; +const formattedLocation = computed(() => { + if (!countryDetails.value) return ''; + + return [countryDetails.value.city, countryDetails.value.name] + .filter(Boolean) + .join(' '); }); const handleFormUpdate = updatedData => { @@ -114,8 +121,12 @@ const onClickViewDetails = () => emit('showContact', props.id); {{ phoneNumber }}
- - {{ countryDetails }} + + + {{ formattedLocation }}