From 7298002da73daf6eee7d857ceaffaf1af95bffb1 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Wed, 14 Feb 2024 03:01:32 -0800 Subject: [PATCH 01/96] fix: Adds forgot password link for login form (#8927) - focus ring not appearing issue - missing forgot password link Fixes: https://linear.app/chatwoot/issue/CW-3090/fix-fixes-issues-with-login-form --- app/javascript/v3/components/Form/Input.vue | 15 ++++++++++++--- app/javascript/v3/components/Form/WithLabel.vue | 3 +++ app/javascript/v3/views/login/Index.vue | 9 ++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/app/javascript/v3/components/Form/Input.vue b/app/javascript/v3/components/Form/Input.vue index b9e73387d..753157ab0 100644 --- a/app/javascript/v3/components/Form/Input.vue +++ b/app/javascript/v3/components/Form/Input.vue @@ -6,24 +6,29 @@ :has-error="hasError" :error-message="errorMessage" > + @@ -48,6 +53,10 @@ export default { type: String, default: 'text', }, + tabindex: { + type: Number, + default: undefined, + }, required: { type: Boolean, default: false, diff --git a/app/javascript/v3/components/Form/WithLabel.vue b/app/javascript/v3/components/Form/WithLabel.vue index d8c339a53..17bdeb202 100644 --- a/app/javascript/v3/components/Form/WithLabel.vue +++ b/app/javascript/v3/components/Form/WithLabel.vue @@ -9,6 +9,9 @@ {{ label }} + + {{ label }} +
diff --git a/app/javascript/v3/views/login/Index.vue b/app/javascript/v3/views/login/Index.vue index d840c27a5..8be74e709 100644 --- a/app/javascript/v3/views/login/Index.vue +++ b/app/javascript/v3/views/login/Index.vue @@ -46,6 +46,7 @@ name="email_address" type="text" data-testid="email_input" + :tabindex="1" required :label="$t('LOGIN.EMAIL.LABEL')" :placeholder="$t('LOGIN.EMAIL.PLACEHOLDER')" @@ -58,19 +59,25 @@ name="password" data-testid="password_input" required + :tabindex="2" :label="$t('LOGIN.PASSWORD.LABEL')" :placeholder="$t('LOGIN.PASSWORD.PLACEHOLDER')" :has-error="$v.credentials.password.$error" @input="$v.credentials.password.$touch" >

- + {{ $t('LOGIN.FORGOT_PASSWORD') }}

From 678c00f2543d2416aea04c0e8127488992e35f7a Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:48:54 -0800 Subject: [PATCH 02/96] fix: Removes default slot for right of label (#8936) --- app/javascript/v3/components/Form/WithLabel.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/javascript/v3/components/Form/WithLabel.vue b/app/javascript/v3/components/Form/WithLabel.vue index 17bdeb202..be7fefffd 100644 --- a/app/javascript/v3/components/Form/WithLabel.vue +++ b/app/javascript/v3/components/Form/WithLabel.vue @@ -9,9 +9,7 @@ {{ label }} - - {{ label }} - +
From 597a4164e8f4c0f3cff56c6c1865236c4cae06b9 Mon Sep 17 00:00:00 2001 From: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:19:06 +0530 Subject: [PATCH 03/96] fix: Editor hyperlink popup is hidden under modal (#8950) --- .../dashboard/components/widgets/WootWriter/Editor.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue index 625fda78e..caa6e235e 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue @@ -753,7 +753,7 @@ export default { } .ProseMirror-prompt { - @apply z-50 bg-slate-25 dark:bg-slate-700 rounded-md border border-solid border-slate-75 dark:border-slate-800; + @apply z-[9999] bg-slate-25 dark:bg-slate-700 rounded-md border border-solid border-slate-75 dark:border-slate-800 shadow-lg; h5 { @apply dark:text-slate-25 text-slate-800; From 3b7b06dbec7a29052d67205d419cd0e3731a1433 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Fri, 16 Feb 2024 14:38:02 +0530 Subject: [PATCH 04/96] feat: Add `meta` to notification model (#8941) * feat: Add meta and description to notification model * chore: remove description --- app/models/notification.rb | 1 + db/migrate/20240215065844_add_meta_to_notifications.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20240215065844_add_meta_to_notifications.rb diff --git a/app/models/notification.rb b/app/models/notification.rb index e0b31b457..f8c6e3d5b 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -4,6 +4,7 @@ # # id :bigint not null, primary key # last_activity_at :datetime +# meta :jsonb # notification_type :integer not null # primary_actor_type :string not null # read_at :datetime diff --git a/db/migrate/20240215065844_add_meta_to_notifications.rb b/db/migrate/20240215065844_add_meta_to_notifications.rb new file mode 100644 index 000000000..e0f93f276 --- /dev/null +++ b/db/migrate/20240215065844_add_meta_to_notifications.rb @@ -0,0 +1,5 @@ +class AddMetaToNotifications < ActiveRecord::Migration[7.0] + def change + add_column :notifications, :meta, :jsonb, default: {} + end +end diff --git a/db/schema.rb b/db/schema.rb index 7d2372554..138ea6835 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_02_07_103014) do +ActiveRecord::Schema[7.0].define(version: 2024_02_15_065844) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "pg_trgm" @@ -750,6 +750,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_02_07_103014) do t.datetime "updated_at", null: false t.datetime "snoozed_until" t.datetime "last_activity_at", default: -> { "CURRENT_TIMESTAMP" } + t.jsonb "meta", default: {} t.index ["account_id"], name: "index_notifications_on_account_id" t.index ["last_activity_at"], name: "index_notifications_on_last_activity_at" t.index ["primary_actor_type", "primary_actor_id"], name: "uniq_primary_actor_per_account_notifications" From 9c07b6dd46387f76b31c5fd7cd6718acff632f41 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 16 Feb 2024 16:44:55 +0530 Subject: [PATCH 05/96] chore: Support for updated_within in ConverationFinder (#8947) - `updated_within' accepts value in seconds and returns all conversations updated in the given period with out pagination. This API will assist in our refetch logic on socket disconnect ref: #8888 --- app/finders/conversation_finder.rb | 8 +++++++- spec/finders/conversation_finder_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb index 0cc5e52d8..f3c85be7a 100644 --- a/app/finders/conversation_finder.rb +++ b/app/finders/conversation_finder.rb @@ -169,6 +169,12 @@ class ConversationFinder ) sort_by, sort_order = SORT_OPTIONS[params[:sort_by]] || SORT_OPTIONS['last_activity_at_desc'] - @conversations.send(sort_by, sort_order).page(current_page).per(ENV.fetch('CONVERSATION_RESULTS_PER_PAGE', '25').to_i) + @conversations = @conversations.send(sort_by, sort_order) + + if params[:updated_within].present? + @conversations.where('conversations.updated_at > ?', Time.zone.now - params[:updated_within].to_i.seconds) + else + @conversations.page(current_page).per(ENV.fetch('CONVERSATION_RESULTS_PER_PAGE', '25').to_i) + end end end diff --git a/spec/finders/conversation_finder_spec.rb b/spec/finders/conversation_finder_spec.rb index 2ce5b6bf9..4d6e9ed40 100644 --- a/spec/finders/conversation_finder_spec.rb +++ b/spec/finders/conversation_finder_spec.rb @@ -146,6 +146,30 @@ describe ConversationFinder do end end + context 'with updated_within' do + let(:params) { { updated_within: 20, assignee_type: 'unassigned', sort_by: 'created_at_asc' } } + + it 'filters based on params, sort order but returns all conversations without pagination with in time range' do + # value of updated_within is in seconds + # write spec based on that + conversations = create_list(:conversation, 50, account: account, + inbox: inbox, assignee: nil, + updated_at: Time.now.utc - 30.seconds, + created_at: Time.now.utc - 30.seconds) + # update updated_at of 27 conversations to be with in 20 seconds + conversations[0..27].each do |conversation| + conversation.update(updated_at: Time.now.utc - 10.seconds) + end + result = conversation_finder.perform + # pagination is not applied + # filters are applied + # modified conversations + 1 conversation created during set up + expect(result[:conversations].length).to be 29 + # ensure that the conversations are sorted by created_at + expect(result[:conversations].first.created_at).to be < result[:conversations].last.created_at + end + end + context 'with pagination' do let(:params) { { status: 'open', assignee_type: 'me', page: 1 } } From a902b49bc5ad9b5c1d292435e0d638d90be7a06a Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 16 Feb 2024 03:31:27 -0800 Subject: [PATCH 06/96] feat: Adds bulk_invite api for onboarding view (#8931) - New API for bulk email invite --- app/javascript/dashboard/api/agents.js | 8 ++++++ .../dashboard/api/specs/agents.spec.js | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/app/javascript/dashboard/api/agents.js b/app/javascript/dashboard/api/agents.js index 7cc5e6d0c..cfc6b36ff 100644 --- a/app/javascript/dashboard/api/agents.js +++ b/app/javascript/dashboard/api/agents.js @@ -1,9 +1,17 @@ +/* global axios */ + import ApiClient from './ApiClient'; class Agents extends ApiClient { constructor() { super('agents', { accountScoped: true }); } + + bulkInvite({ emails }) { + return axios.post(`${this.url}/bulk_create`, { + emails, + }); + } } export default new Agents(); diff --git a/app/javascript/dashboard/api/specs/agents.spec.js b/app/javascript/dashboard/api/specs/agents.spec.js index 7cf1bdd0e..20dd36688 100644 --- a/app/javascript/dashboard/api/specs/agents.spec.js +++ b/app/javascript/dashboard/api/specs/agents.spec.js @@ -10,4 +10,29 @@ describe('#AgentAPI', () => { expect(agents).toHaveProperty('update'); expect(agents).toHaveProperty('delete'); }); + + describe('API calls', () => { + const originalAxios = window.axios; + const axiosMock = { + post: jest.fn(() => Promise.resolve()), + }; + + beforeEach(() => { + window.axios = axiosMock; + }); + + afterEach(() => { + window.axios = originalAxios; + }); + + it('#bulkInvite', () => { + agents.bulkInvite({ emails: ['hello@hi.com'] }); + expect(axiosMock.post).toHaveBeenCalledWith( + '/api/v1/agents/bulk_create', + { + emails: ['hello@hi.com'], + } + ); + }); + }); }); From fe744abe268e9b4d51d85ff21e3ebb2c14f7e085 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Fri, 16 Feb 2024 17:30:54 +0530 Subject: [PATCH 07/96] fix: Mark notification as unread after the un-snooze (#8917) --- app/jobs/notification/reopen_snoozed_notifications_job.rb | 2 +- spec/jobs/notification/reopen_snoozed_notifications_job_spec.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/jobs/notification/reopen_snoozed_notifications_job.rb b/app/jobs/notification/reopen_snoozed_notifications_job.rb index 89b245187..05235b62b 100644 --- a/app/jobs/notification/reopen_snoozed_notifications_job.rb +++ b/app/jobs/notification/reopen_snoozed_notifications_job.rb @@ -4,7 +4,7 @@ class Notification::ReopenSnoozedNotificationsJob < ApplicationJob def perform # rubocop:disable Rails/SkipsModelValidations Notification.where(snoozed_until: 3.days.ago..Time.current) - .update_all(snoozed_until: nil, updated_at: Time.current, last_activity_at: Time.current) + .update_all(snoozed_until: nil, updated_at: Time.current, last_activity_at: Time.current, read_at: nil) # rubocop:enable Rails/SkipsModelValidations end end diff --git a/spec/jobs/notification/reopen_snoozed_notifications_job_spec.rb b/spec/jobs/notification/reopen_snoozed_notifications_job_spec.rb index 2b210ef2b..024c3d0d6 100644 --- a/spec/jobs/notification/reopen_snoozed_notifications_job_spec.rb +++ b/spec/jobs/notification/reopen_snoozed_notifications_job_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Notification::ReopenSnoozedNotificationsJob do expect(snoozed_till_5_minutes_ago.reload.snoozed_until).to be_nil expect(snoozed_till_tomorrow.reload.snoozed_until.to_date).to eq 1.day.from_now.to_date expect(snoozed_indefinitely.reload.snoozed_until).to be_nil + expect(snoozed_indefinitely.reload.read_at).to be_nil end end end From 94892e71687fab7d21843e892dac05e45ded3ee5 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Sat, 17 Feb 2024 01:41:39 +0530 Subject: [PATCH 08/96] chore: Fix flaky spec relying on unguaranteed order (#8956) --- .../api/v1/accounts/contacts_controller_spec.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb index ace9e8e03..e5e298747 100644 --- a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb @@ -50,8 +50,11 @@ RSpec.describe 'Contacts API', type: :request do expect(response).to have_http_status(:success) response_body = response.parsed_body - expect(response_body['payload'].first['email']).to eq(contact.email) - expect(response_body['payload'].first['contact_inboxes'].blank?).to be(true) + + contact_emails = response_body['payload'].pluck('email') + contact_inboxes = response_body['payload'].pluck('contact_inboxes').flatten.compact + expect(contact_emails).to include(contact.email) + expect(contact_inboxes).to eq([]) end it 'returns all contacts with company name desc order' do From 6eb06377ccd0a885a9e4a3f04615028f49209d4b Mon Sep 17 00:00:00 2001 From: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Date: Fri, 16 Feb 2024 12:19:08 -0800 Subject: [PATCH 09/96] feat: Add textarea component to use in forms (#8932) Co-authored-by: Pranav --- .../v3/components/Form/Textarea.vue | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 app/javascript/v3/components/Form/Textarea.vue diff --git a/app/javascript/v3/components/Form/Textarea.vue b/app/javascript/v3/components/Form/Textarea.vue new file mode 100644 index 000000000..e43c411f5 --- /dev/null +++ b/app/javascript/v3/components/Form/Textarea.vue @@ -0,0 +1,83 @@ +