From 71d1f229f086f5035877cef97f344495a292773c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 16:37:05 +0530 Subject: [PATCH 01/67] chore: Bump nokogiri from 1.11.3 to 1.11.4 (#2293) Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.11.3 to 1.11.4. - [Release notes](https://github.com/sparklemotion/nokogiri/releases) - [Changelog](https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md) - [Commits](https://github.com/sparklemotion/nokogiri/compare/v1.11.3...v1.11.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sojan Jose --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e3061c705..222f28ec5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -350,7 +350,7 @@ GEM connection_pool (~> 2.2) netrc (0.11.0) nio4r (2.5.7) - nokogiri (1.11.3) + nokogiri (1.11.4) mini_portile2 (~> 2.5.0) racc (~> 1.4) oauth (0.5.6) From af8e681f2a9730272ff1d95400a36c994328eef0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 May 2021 17:14:15 +0530 Subject: [PATCH 02/67] chore: Bump puma from 4.3.6 to 4.3.8 (#2294) Bumps [puma](https://github.com/puma/puma) from 4.3.6 to 4.3.8. - [Release notes](https://github.com/puma/puma/releases) - [Changelog](https://github.com/puma/puma/blob/master/History.md) - [Commits](https://github.com/puma/puma/compare/v4.3.6...v4.3.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sojan Jose --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 222f28ec5..abb305465 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -368,7 +368,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (4.0.6) - puma (4.3.6) + puma (4.3.8) nio4r (~> 2.0) pundit (2.1.0) activesupport (>= 3.0.0) From c681e8a01bd226c4f513211159305ee733fa92d0 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Thu, 20 May 2021 13:51:46 +0530 Subject: [PATCH 03/67] chore: Refactor button styles (#2259) --- .../assets/scss/_foundation-custom.scss | 10 +- .../assets/scss/_foundation-settings.scss | 4 +- .../dashboard/assets/scss/_woot.scss | 23 ++- app/javascript/dashboard/assets/scss/app.scss | 19 -- .../dashboard/assets/scss/storybook.scss | 2 + .../assets/scss/widgets/_buttons.scss | 87 +++++--- .../assets/scss/widgets/_conv-header.scss | 3 +- .../scss/widgets/_conversation-view.scss | 1 - .../dashboard/components/ChatList.vue | 9 +- .../components/layout/AvailabilityStatus.vue | 2 + .../dashboard/components/layout/Sidebar.vue | 6 - .../layout/sidebarComponents/OptionsMenu.vue | 30 ++- .../dashboard/components/ui/WootButton.vue | 47 +++-- .../components/widgets/TableFooter.vue | 64 ++---- .../widgets/conversation/ConversationBox.vue | 2 +- .../conversation/ConversationHeader.vue | 8 +- .../widgets/conversation/bubble/Actions.vue | 2 +- .../contacts/components/ContactsTable.vue | 7 +- .../dashboard/contacts/components/Header.vue | 3 +- .../conversation/contact/ContactInfoRow.vue | 14 +- .../dashboard/settings/SettingsHeader.vue | 6 +- .../dashboard/settings/agents/EditAgent.vue | 9 +- .../dashboard/settings/agents/Index.vue | 38 ++-- .../dashboard/settings/canned/Index.vue | 44 +++-- .../routes/dashboard/settings/inbox/Index.vue | 28 +-- .../inbox/components/CampaignsTable.vue | 6 +- .../settings/integrations/Webhook.vue | 10 +- .../dashboard/settings/labels/Index.vue | 39 ++-- .../dashboard/settings/reports/Index.vue | 9 +- .../routes/dashboard/settings/teams/Index.vue | 29 +-- .../components/ui/dropdown/DropdownItem.vue | 4 +- stories/Sections/Button.stories.mdx | 185 ++++++++++++++++++ 32 files changed, 490 insertions(+), 260 deletions(-) create mode 100644 stories/Sections/Button.stories.mdx diff --git a/app/javascript/dashboard/assets/scss/_foundation-custom.scss b/app/javascript/dashboard/assets/scss/_foundation-custom.scss index 9c398e078..61ead3492 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-custom.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-custom.scss @@ -6,13 +6,11 @@ border-radius: 1000px; } - &.grey-btn { - color: $color-gray; - &:hover { - color: $color-light-gray; - } - } +} + +.button-wrapper .button.link.grey-btn { + margin-left: var(--space-normal); } .tooltip { diff --git a/app/javascript/dashboard/assets/scss/_foundation-settings.scss b/app/javascript/dashboard/assets/scss/_foundation-settings.scss index dbdb630c0..56d82896b 100644 --- a/app/javascript/dashboard/assets/scss/_foundation-settings.scss +++ b/app/javascript/dashboard/assets/scss/_foundation-settings.scss @@ -238,7 +238,7 @@ $breadcrumbs-item-slash: true; // 11. Button // ---------- -$button-padding: var(--space-one) var(--space-slab); +$button-padding: var(--space-smaller) 1em; $button-margin: 0 0 $global-margin 0; $button-fill: solid; $button-background: $primary-color; @@ -246,7 +246,7 @@ $button-background-hover: scale-color($button-background, $lightness: -15%); $button-color: $white; $button-color-alt: $white; $button-radius: var(--border-radius-normal); -$button-sizes: (tiny: var(--font-size-nano), +$button-sizes: (tiny: var(--font-size-micro), small: var(--font-size-mini), default: var(--font-size-small), large: var(--font-size-medium)); diff --git a/app/javascript/dashboard/assets/scss/_woot.scss b/app/javascript/dashboard/assets/scss/_woot.scss index e53ceef73..7ecec198b 100644 --- a/app/javascript/dashboard/assets/scss/_woot.scss +++ b/app/javascript/dashboard/assets/scss/_woot.scss @@ -1,8 +1,28 @@ +@import 'shared/assets/fonts/inter'; +@import 'shared/assets/stylesheets/colors'; +@import 'shared/assets/stylesheets/spacing'; +@import 'shared/assets/stylesheets/font-size'; +@import 'shared/assets/stylesheets/font-weights'; +@import 'shared/assets/stylesheets/shadows'; +@import 'shared/assets/stylesheets/border-radius'; + +@import 'variables'; + +@import 'mixins'; +@import 'foundation-settings'; +@import 'helper-classes'; + +@import 'foundation-sites/scss/foundation'; +@import '~bourbon/core/bourbon'; + +@include foundation-everything($flex: true); + + @import 'typography'; @import 'layout'; @import 'animations'; - @import 'foundation-custom'; + @import 'widgets/buttons'; @import 'widgets/conv-header'; @import 'widgets/conversation-card'; @@ -27,3 +47,4 @@ @import 'plugins/multiselect'; @import 'plugins/dropdown'; @import '@chatwoot/prosemirror-schema/src/woot-editor.css'; +@import '~shared/assets/stylesheets/ionicons'; diff --git a/app/javascript/dashboard/assets/scss/app.scss b/app/javascript/dashboard/assets/scss/app.scss index dbf3a6e78..13c3dd557 100644 --- a/app/javascript/dashboard/assets/scss/app.scss +++ b/app/javascript/dashboard/assets/scss/app.scss @@ -1,20 +1 @@ -@import 'shared/assets/fonts/inter'; -@import 'shared/assets/stylesheets/colors'; -@import 'shared/assets/stylesheets/spacing'; -@import 'shared/assets/stylesheets/font-size'; -@import 'shared/assets/stylesheets/font-weights'; -@import 'shared/assets/stylesheets/shadows'; -@import 'shared/assets/stylesheets/border-radius'; -@import 'variables'; - -@import '~spinkit/scss/spinners/7-three-bounce'; -@import '~shared/assets/stylesheets/ionicons'; - -@import 'mixins'; -@import 'foundation-settings'; -@import 'helper-classes'; -@import 'foundation-sites/scss/foundation'; -@import '~bourbon/core/bourbon'; - -@include foundation-everything($flex: true); @import 'woot'; diff --git a/app/javascript/dashboard/assets/scss/storybook.scss b/app/javascript/dashboard/assets/scss/storybook.scss index fbe88bc3f..f6a9d5eb3 100644 --- a/app/javascript/dashboard/assets/scss/storybook.scss +++ b/app/javascript/dashboard/assets/scss/storybook.scss @@ -14,6 +14,8 @@ @import 'foundation-settings'; @import 'helper-classes'; @import 'foundation-sites/scss/foundation'; + +@include foundation-prototype-spacing; @import '~bourbon/core/bourbon'; @include foundation-everything($flex: true); diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss index a599721c2..1d6b12631 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss @@ -1,4 +1,7 @@ .button { + align-items: center; + display: inline-flex; + height: 4.0rem; margin-bottom: 0; &.button--emoji { @@ -19,52 +22,76 @@ } } - - - &.icon { - padding-left: $space-normal; - padding-right: $space-normal; - - i { - padding-right: $space-small; - } + .spinner { + padding: 0 var(--space-small); } - &.nice { - border-radius: $space-smaller; + .icon+.button__content { + padding-left: var(--space-small); } - &.hollow { - &.link { - border-color: transparent; - padding-left: 0; - - &:hover, - &:focus { - border-color: transparent; - } - } - } - - >.icon { - font-size: $font-size-default; - } - - &.tiny { - font-size: $font-size-mini; - padding: $space-small $space-slab; + &.expanded { + display: flex; + justify-content: center; } &.round { border-radius: $space-larger; } + // @TODO Use with link + &.compact { padding-bottom: 0; padding-top: 0; } + + // Smooth style + &.smooth { + @include button-style(var(--w-100), var(--w-50), var(--w-700)); + + + &.secondary { + @include button-style(var(--s-100), var(--s-50), var(--s-700)); + } + + &.success { + @include button-style(var(--g-100), var(--g-50), var(--g-700)); + } + + &.alert { + @include button-style(var(--r-100), var(--r-50), var(--r-700)); + } + + &.warning { + @include button-style(var(--y-200), var(--y-100), var(--y-900)); + } + } + + // Sizes + &.tiny { + height: var(--space-medium); + } + + &.small { + height: var(--space-large); + } + + &.large { + height: var(--space-larger); + } + + &.link { + height: auto; + margin: 0; + padding: 0; + } + + } + +// @TDOD move to utility file .button--fixed-right-top { position: fixed; right: $space-small; diff --git a/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss b/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss index 1c081c089..f286c7bc0 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conv-header.scss @@ -2,11 +2,11 @@ $resolve-button-width: 13.2rem; // Conversation header - Light BG .conv-header { - @include padding($space-small $space-normal); @include background-white; @include flex; @include flex-align($x: justify, $y: middle); @include border-normal-bottom; + padding: var(--space-small) var(--space-normal); .multiselect-box { @include flex; @@ -70,6 +70,7 @@ $resolve-button-width: 13.2rem; .header-actions-wrap { + align-items: center; display: flex; flex-direction: row; flex-grow: 1; diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss index bc3e20254..d79cfe80d 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss @@ -76,7 +76,6 @@ .status--filter { @include padding($zero null $zero $space-normal); - @include round-corner; @include margin($space-smaller $space-slab $zero $zero); background-color: $color-background-light; border: 1px solid $color-border; diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 3b7fe0b8c..0aba2739f 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -32,13 +32,14 @@ -
{{ $t('CHAT_LIST.LOAD_MORE_CONVERSATIONS') }} -
+

-@import '~dashboard/assets/scss/app.scss'; +@import '~dashboard/assets/scss/woot'; .spinner { margin-top: var(--space-normal); margin-bottom: var(--space-normal); diff --git a/app/javascript/dashboard/components/layout/AvailabilityStatus.vue b/app/javascript/dashboard/components/layout/AvailabilityStatus.vue index 72488b82e..8587bacc8 100644 --- a/app/javascript/dashboard/components/layout/AvailabilityStatus.vue +++ b/app/javascript/dashboard/components/layout/AvailabilityStatus.vue @@ -22,6 +22,8 @@ > - + - + - + {{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }} - + {{ $t('SIDEBAR_ITEMS.LOGOUT') }} - + diff --git a/app/javascript/dashboard/components/ui/WootButton.vue b/app/javascript/dashboard/components/ui/WootButton.vue index 4df156f3a..a71e5388e 100644 --- a/app/javascript/dashboard/components/ui/WootButton.vue +++ b/app/javascript/dashboard/components/ui/WootButton.vue @@ -1,13 +1,7 @@ diff --git a/app/javascript/dashboard/modules/contact/components/ManageLayout.vue b/app/javascript/dashboard/modules/contact/components/ManageLayout.vue index 87884e14e..363ed19df 100644 --- a/app/javascript/dashboard/modules/contact/components/ManageLayout.vue +++ b/app/javascript/dashboard/modules/contact/components/ManageLayout.vue @@ -55,10 +55,14 @@ export default { .wrap { @include three-column-grid(27.2rem); - background: var(--color-background); + min-height: 0; + + background: var(--color-background-light); border-top: 1px solid var(--color-border); } - +.left { + overflow: auto; +} .center { border-right: 1px solid var(--color-border); border-left: 1px solid var(--color-border); diff --git a/app/javascript/dashboard/modules/contact/stories/AddCustomAttribute.stories.js b/app/javascript/dashboard/modules/contact/stories/AddCustomAttribute.stories.js new file mode 100644 index 000000000..603cb5167 --- /dev/null +++ b/app/javascript/dashboard/modules/contact/stories/AddCustomAttribute.stories.js @@ -0,0 +1,32 @@ +import AddCustomAttribute from '../components/AddCustomAttribute'; +import { action } from '@storybook/addon-actions'; + +export default { + title: 'Components/Contact/AddCustomAttribute', + component: AddCustomAttribute, + argTypes: { + show: { + defaultValue: true, + control: { + type: 'boolean', + }, + }, + isCreating: { + defaultValue: false, + control: { + type: 'boolean', + }, + }, + }, +}; + +const Template = (args, { argTypes }) => ({ + props: Object.keys(argTypes), + components: { AddCustomAttribute }, + template: '', +}); + +export const DefaultAttribute = Template.bind({}); +DefaultAttribute.args = { + onCreate: action('edit'), +}; diff --git a/app/javascript/dashboard/modules/contact/stories/ContactFields.stories.js b/app/javascript/dashboard/modules/contact/stories/ContactFields.stories.js index 01a99f254..3b0b02be5 100644 --- a/app/javascript/dashboard/modules/contact/stories/ContactFields.stories.js +++ b/app/javascript/dashboard/modules/contact/stories/ContactFields.stories.js @@ -10,7 +10,7 @@ const Template = (args, { argTypes }) => ({ props: Object.keys(argTypes), components: { ContactFields }, template: - '', + '', }); export const DefaultContactFields = Template.bind({}); @@ -39,4 +39,5 @@ DefaultContactFields.args = { }, }, onUpdate: action('update'), + onCreate: action('create'), }; diff --git a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue index 8b7707b3c..a94068801 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue @@ -73,6 +73,9 @@ export default { From ac2c5c93b9b7fb76dac60c85d30d5bf24d6a4713 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Fri, 21 May 2021 19:49:57 +0530 Subject: [PATCH 10/67] chore: Update webhook delete button styles(#2322) * Chore: Fixes style issue of webhook button * Review fixes --- .../dashboard/settings/integrations/Webhook.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue index 87cee8e80..b64f5140e 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue @@ -43,14 +43,14 @@

- + + {{ $t('INTEGRATION_SETTINGS.WEBHOOK.DELETE.BUTTON_TEXT') }} +
From 58dd6d37e0db747199c58137c2140d6a7c574689 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 21 May 2021 23:52:38 +0530 Subject: [PATCH 11/67] chore: Update language translations (#2321) --- .../dashboard/i18n/locale/ar/contact.json | 24 +++- .../dashboard/i18n/locale/ca/contact.json | 24 +++- .../dashboard/i18n/locale/cs/contact.json | 24 +++- .../dashboard/i18n/locale/da/contact.json | 24 +++- .../dashboard/i18n/locale/de/contact.json | 24 +++- .../dashboard/i18n/locale/el/campaign.json | 4 +- .../dashboard/i18n/locale/el/contact.json | 32 +++-- .../dashboard/i18n/locale/es/contact.json | 24 +++- .../dashboard/i18n/locale/fa/contact.json | 24 +++- .../dashboard/i18n/locale/fi/contact.json | 24 +++- .../dashboard/i18n/locale/fr/contact.json | 24 +++- .../dashboard/i18n/locale/hi/contact.json | 24 +++- .../dashboard/i18n/locale/hu/contact.json | 24 +++- .../dashboard/i18n/locale/id/contact.json | 24 +++- .../dashboard/i18n/locale/it/contact.json | 24 +++- .../dashboard/i18n/locale/ja/contact.json | 24 +++- .../dashboard/i18n/locale/ko/contact.json | 24 +++- .../dashboard/i18n/locale/ml/contact.json | 24 +++- .../dashboard/i18n/locale/ne/contact.json | 24 +++- .../dashboard/i18n/locale/nl/contact.json | 24 +++- .../dashboard/i18n/locale/no/contact.json | 24 +++- .../dashboard/i18n/locale/pl/contact.json | 24 +++- .../dashboard/i18n/locale/pt/contact.json | 24 +++- .../dashboard/i18n/locale/pt_BR/contact.json | 24 +++- .../dashboard/i18n/locale/ro/contact.json | 24 +++- .../dashboard/i18n/locale/ru/contact.json | 24 +++- .../dashboard/i18n/locale/ru/report.json | 6 +- .../dashboard/i18n/locale/sk/contact.json | 24 +++- .../dashboard/i18n/locale/sv/contact.json | 24 +++- .../dashboard/i18n/locale/ta/contact.json | 24 +++- .../dashboard/i18n/locale/th/campaign.json | 74 ++++++------ .../dashboard/i18n/locale/th/chatlist.json | 4 +- .../dashboard/i18n/locale/th/contact.json | 110 ++++++++++-------- .../dashboard/i18n/locale/th/inboxMgmt.json | 2 +- .../dashboard/i18n/locale/th/settings.json | 2 +- .../dashboard/i18n/locale/tr/contact.json | 24 +++- .../dashboard/i18n/locale/uk/contact.json | 24 +++- .../dashboard/i18n/locale/vi/contact.json | 24 +++- .../dashboard/i18n/locale/zh_CN/contact.json | 24 +++- .../dashboard/i18n/locale/zh_TW/contact.json | 24 +++- app/javascript/widget/i18n/locale/ar.json | 3 +- app/javascript/widget/i18n/locale/ca.json | 3 +- app/javascript/widget/i18n/locale/cs.json | 3 +- app/javascript/widget/i18n/locale/da.json | 3 +- app/javascript/widget/i18n/locale/de.json | 3 +- app/javascript/widget/i18n/locale/el.json | 3 +- app/javascript/widget/i18n/locale/es.json | 3 +- app/javascript/widget/i18n/locale/fa.json | 3 +- app/javascript/widget/i18n/locale/fi.json | 3 +- app/javascript/widget/i18n/locale/fr.json | 3 +- app/javascript/widget/i18n/locale/hi.json | 3 +- app/javascript/widget/i18n/locale/hu.json | 3 +- app/javascript/widget/i18n/locale/id.json | 3 +- app/javascript/widget/i18n/locale/it.json | 3 +- app/javascript/widget/i18n/locale/ja.json | 3 +- app/javascript/widget/i18n/locale/ko.json | 3 +- app/javascript/widget/i18n/locale/ml.json | 3 +- app/javascript/widget/i18n/locale/ne.json | 3 +- app/javascript/widget/i18n/locale/nl.json | 3 +- app/javascript/widget/i18n/locale/no.json | 3 +- app/javascript/widget/i18n/locale/pl.json | 3 +- app/javascript/widget/i18n/locale/pt.json | 3 +- app/javascript/widget/i18n/locale/pt_BR.json | 3 +- app/javascript/widget/i18n/locale/ro.json | 3 +- app/javascript/widget/i18n/locale/ru.json | 3 +- app/javascript/widget/i18n/locale/sk.json | 3 +- app/javascript/widget/i18n/locale/sv.json | 3 +- app/javascript/widget/i18n/locale/ta.json | 3 +- app/javascript/widget/i18n/locale/th.json | 3 +- app/javascript/widget/i18n/locale/tr.json | 3 +- app/javascript/widget/i18n/locale/uk.json | 3 +- app/javascript/widget/i18n/locale/vi.json | 3 +- app/javascript/widget/i18n/locale/zh_CN.json | 3 +- app/javascript/widget/i18n/locale/zh_TW.json | 3 +- 74 files changed, 875 insertions(+), 229 deletions(-) diff --git a/app/javascript/dashboard/i18n/locale/ar/contact.json b/app/javascript/dashboard/i18n/locale/ar/contact.json index 2218c93c4..51bd4e9d8 100644 --- a/app/javascript/dashboard/i18n/locale/ar/contact.json +++ b/app/javascript/dashboard/i18n/locale/ar/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "لا توجد محادثات سابقة مرتبطة بجهة الاتصال هذه.", "TITLE": "المحادثات السابقة" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "سمات مخصصة" - }, "LABELS": { "TITLE": "وسوم المحادثة", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "سمات مخصصة", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "إلغاء", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ca/contact.json b/app/javascript/dashboard/i18n/locale/ca/contact.json index 996adbfc2..2e13eabcd 100644 --- a/app/javascript/dashboard/i18n/locale/ca/contact.json +++ b/app/javascript/dashboard/i18n/locale/ca/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "No hi han converses prèvies associades a aquest contacte.", "TITLE": "Converses prèvies" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Atributs personalitzats" - }, "LABELS": { "TITLE": "Etiquetes de converses", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Atributs personalitzats", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancel·la", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/cs/contact.json b/app/javascript/dashboard/i18n/locale/cs/contact.json index 46bb54580..958167be3 100644 --- a/app/javascript/dashboard/i18n/locale/cs/contact.json +++ b/app/javascript/dashboard/i18n/locale/cs/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "K tomuto kontaktu nejsou přiřazeny žádné předchozí konverzace.", "TITLE": "Předchozí konverzace" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Vlastní atributy" - }, "LABELS": { "TITLE": "Štítky konverzace", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Vlastní atributy", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Zrušit", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/da/contact.json b/app/javascript/dashboard/i18n/locale/da/contact.json index ef1f5595e..e1712ed49 100644 --- a/app/javascript/dashboard/i18n/locale/da/contact.json +++ b/app/javascript/dashboard/i18n/locale/da/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Der er ingen tidligere samtaler tilknyttet denne kontakt.", "TITLE": "Tidligere Samtaler" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Brugerdefinerede Egenskaber" - }, "LABELS": { "TITLE": "Samtale Etiketter", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Brugerdefinerede Egenskaber", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Annuller", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/de/contact.json b/app/javascript/dashboard/i18n/locale/de/contact.json index 341d905cb..82ced872a 100644 --- a/app/javascript/dashboard/i18n/locale/de/contact.json +++ b/app/javascript/dashboard/i18n/locale/de/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Es sind keine vorherigen Gespräche mit diesem Kontakt verbunden.", "TITLE": "Vorherige Gespräche" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Benutzerdefinierte Attribute" - }, "LABELS": { "TITLE": "Konversationsetiketten", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "Alle Notizen anzeigen" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Benutzerdefinierte Attribute", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Stornieren", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/el/campaign.json b/app/javascript/dashboard/i18n/locale/el/campaign.json index b44520dd4..7e027454e 100644 --- a/app/javascript/dashboard/i18n/locale/el/campaign.json +++ b/app/javascript/dashboard/i18n/locale/el/campaign.json @@ -43,10 +43,10 @@ } }, "EDIT": { - "TITLE": "Edit campaign", + "TITLE": "Επεξεργασία καμπάνιας", "UPDATE_BUTTON_TEXT": "Ενημέρωση", "API": { - "SUCCESS_MESSAGE": "Campaign updated successfully", + "SUCCESS_MESSAGE": "Η ετικέτα ενημερώθηκε επιτυχώς", "ERROR_MESSAGE": "Υπήρξε ένα σφάλμα, παρακαλώ προσπαθήστε ξανά" } }, diff --git a/app/javascript/dashboard/i18n/locale/el/contact.json b/app/javascript/dashboard/i18n/locale/el/contact.json index 03af6312f..199080639 100644 --- a/app/javascript/dashboard/i18n/locale/el/contact.json +++ b/app/javascript/dashboard/i18n/locale/el/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Δεν υπάρχουν προηγούμενες συνομιλίες που σχετίζονται με αυτήν την επαφή.", "TITLE": "Προηγούμενες συνομιλίες" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Προσαρμοζόμενες Ιδιότητες" - }, "LABELS": { "TITLE": "Ετικέτες συνομιλίας", "MODAL": { @@ -28,16 +25,16 @@ "INACTIVE_LABELS": "Υπάρχουν διαθέσιμες ετικέτες στον λογαριασμό", "REMOVE": "Πατήστε στο εικονίδιο X για να απομακρύνετε την ετικέτα", "ADD": "Πατήστε στο εικονίδιο + για να προσθέστε την ετικέτα", - "ADD_BUTTON": "Add Labels", + "ADD_BUTTON": "Προσθήκη Ετικετών", "UPDATE_BUTTON": "Ενημέρωση Ετικετών", "UPDATE_ERROR": "Δεν μπορούν να ενημερωθούν οι ετικέτες, προσπαθήστε ξανά." }, "NO_LABELS_TO_ADD": "Δεν υπάρχουν άλλες ετικέτες στον λογαριασμό.", "NO_AVAILABLE_LABELS": "Δεν υπάρχουν προστεθεί ετικέτες στην συνομιλία.", "LABEL_SELECT": { - "TITLE": "Add Labels", - "PLACEHOLDER": "Search labels", - "NO_RESULT": "No labels found" + "TITLE": "Προσθήκη Ετικετών", + "PLACEHOLDER": "Αναζήτηση ετικετών", + "NO_RESULT": "Δεν βρέθηκαν ετικέτες" } }, "MUTE_CONTACT": "Σίγαση Συνομιλίας", @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "Εμφάνιση όλων των σημειώσεων" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Προσαρμοζόμενες Ιδιότητες", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Άκυρο", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/es/contact.json b/app/javascript/dashboard/i18n/locale/es/contact.json index 4b795bac1..b8f1b2ccb 100644 --- a/app/javascript/dashboard/i18n/locale/es/contact.json +++ b/app/javascript/dashboard/i18n/locale/es/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "No hay conversaciones previas asociadas a este contacto.", "TITLE": "Conversaciones anteriores" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Atributos personalizados" - }, "LABELS": { "TITLE": "Etiquetas de conversación", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "Ver todas las notas" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Atributos personalizados", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancelar", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/fa/contact.json b/app/javascript/dashboard/i18n/locale/fa/contact.json index c95629749..bbe09162c 100644 --- a/app/javascript/dashboard/i18n/locale/fa/contact.json +++ b/app/javascript/dashboard/i18n/locale/fa/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "این اولین گفتگوی این کاربر است.", "TITLE": "گفتگوهای قبلی" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "ویژگی‌های سفارشی" - }, "LABELS": { "TITLE": "برچسب‌های گفتگو", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "ویژگی‌های سفارشی", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "انصراف", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/fi/contact.json b/app/javascript/dashboard/i18n/locale/fi/contact.json index 9f4783221..48db74762 100644 --- a/app/javascript/dashboard/i18n/locale/fi/contact.json +++ b/app/javascript/dashboard/i18n/locale/fi/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Tähän yhteystietoon ei liity aikaisempia keskusteluja.", "TITLE": "Edelliset keskustelut" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Mukautetut attribuutit" - }, "LABELS": { "TITLE": "Keskustelutunnisteet", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Mukautetut attribuutit", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Peruuta", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/fr/contact.json b/app/javascript/dashboard/i18n/locale/fr/contact.json index 715eda37d..dff214bed 100644 --- a/app/javascript/dashboard/i18n/locale/fr/contact.json +++ b/app/javascript/dashboard/i18n/locale/fr/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Il n'y a aucune conversation précédente associée à ce contact.", "TITLE": "Conversations précédentes" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Attributs personnalisés" - }, "LABELS": { "TITLE": "Étiquettes de conversation", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Attributs personnalisés", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Annuler", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/hi/contact.json b/app/javascript/dashboard/i18n/locale/hi/contact.json index 6a94011a5..80a33f25a 100644 --- a/app/javascript/dashboard/i18n/locale/hi/contact.json +++ b/app/javascript/dashboard/i18n/locale/hi/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "There are no previous conversations associated to this contact.", "TITLE": "Previous Conversations" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Conversation Labels", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancel", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/hu/contact.json b/app/javascript/dashboard/i18n/locale/hu/contact.json index 378a2413a..c91e66fdb 100644 --- a/app/javascript/dashboard/i18n/locale/hu/contact.json +++ b/app/javascript/dashboard/i18n/locale/hu/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Nincs megelőző beszélgetés ezzel a kontakttal.", "TITLE": "Korábbi beszélgetések" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Egyedi atribútumok" - }, "LABELS": { "TITLE": "Beszélgetés cimkék", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Egyedi atribútumok", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Mégse", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/id/contact.json b/app/javascript/dashboard/i18n/locale/id/contact.json index 56f590502..3cf9c5872 100644 --- a/app/javascript/dashboard/i18n/locale/id/contact.json +++ b/app/javascript/dashboard/i18n/locale/id/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Tidak ada percakapan sebelumnya yang terkait dengan kontak ini.", "TITLE": "Percakapan Sebelumnya" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Atribut Kustom" - }, "LABELS": { "TITLE": "Label Percakapan", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Atribut Kustom", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Batalkan", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/it/contact.json b/app/javascript/dashboard/i18n/locale/it/contact.json index 1bb13b561..23beb5c9f 100644 --- a/app/javascript/dashboard/i18n/locale/it/contact.json +++ b/app/javascript/dashboard/i18n/locale/it/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Non ci sono precedenti conversazioni associate a questo contatto.", "TITLE": "Conversazioni precedenti" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Etichette conversazione", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "annulla", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ja/contact.json b/app/javascript/dashboard/i18n/locale/ja/contact.json index 4975d8f6d..ed8639c1b 100644 --- a/app/javascript/dashboard/i18n/locale/ja/contact.json +++ b/app/javascript/dashboard/i18n/locale/ja/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "この連絡先に関連付けられている以前の会話はありません。", "TITLE": "前の会話" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "カスタム属性" - }, "LABELS": { "TITLE": "会話のラベル", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "カスタム属性", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "キャンセル", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ko/contact.json b/app/javascript/dashboard/i18n/locale/ko/contact.json index 52ac02c1b..7af037f32 100644 --- a/app/javascript/dashboard/i18n/locale/ko/contact.json +++ b/app/javascript/dashboard/i18n/locale/ko/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "이 연락처와 관련된 이전 대화가 없습니다.", "TITLE": "이전 대화" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "사용자 지정 특성" - }, "LABELS": { "TITLE": "대화 라벨", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "사용자 지정 특성", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "취소", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ml/contact.json b/app/javascript/dashboard/i18n/locale/ml/contact.json index d2af1ebb6..cb96143b5 100644 --- a/app/javascript/dashboard/i18n/locale/ml/contact.json +++ b/app/javascript/dashboard/i18n/locale/ml/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "ഈ കോൺടാക്റ്റുമായി മുമ്പത്തെ സംഭാഷണങ്ങളൊന്നും ബന്ധപ്പെടുത്തിയിട്ടില്ല.", "TITLE": "മുമ്പത്തെ സംഭാഷണങ്ങൾ" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "ഇഷ്‌ടാനുസൃത ആട്രിബ്യൂട്ടുകൾ" - }, "LABELS": { "TITLE": "സംഭാഷണ ലേബലുകൾ", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "ഇഷ്‌ടാനുസൃത ആട്രിബ്യൂട്ടുകൾ", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "റദ്ദാക്കുക", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ne/contact.json b/app/javascript/dashboard/i18n/locale/ne/contact.json index b00c7c3d4..755bc1969 100644 --- a/app/javascript/dashboard/i18n/locale/ne/contact.json +++ b/app/javascript/dashboard/i18n/locale/ne/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "There are no previous conversations associated to this contact.", "TITLE": "Previous Conversations" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Conversation Labels", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancel", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/nl/contact.json b/app/javascript/dashboard/i18n/locale/nl/contact.json index 848a1523c..8bee921d3 100644 --- a/app/javascript/dashboard/i18n/locale/nl/contact.json +++ b/app/javascript/dashboard/i18n/locale/nl/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Er zijn geen eerdere gesprekken gekoppeld aan dit contact.", "TITLE": "Vorige gesprekken" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Labels voor gesprekken", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Annuleren", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/no/contact.json b/app/javascript/dashboard/i18n/locale/no/contact.json index 2da471d42..5bff9e747 100644 --- a/app/javascript/dashboard/i18n/locale/no/contact.json +++ b/app/javascript/dashboard/i18n/locale/no/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Det er ingen tidligere samtaler knyttet til denne kontakten.", "TITLE": "Tidligere samtaler" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Egendefinerte verdier" - }, "LABELS": { "TITLE": "Samtaleetiketter", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Egendefinerte verdier", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Avbryt", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/pl/contact.json b/app/javascript/dashboard/i18n/locale/pl/contact.json index f761ab4fc..bcdabc21c 100644 --- a/app/javascript/dashboard/i18n/locale/pl/contact.json +++ b/app/javascript/dashboard/i18n/locale/pl/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Brak poprzednich rozmów powiązanych z tym kontaktem.", "TITLE": "Poprzednie rozmowy" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Niestandardowe atrybuty" - }, "LABELS": { "TITLE": "Etykiety konwersacji", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "Wyświetl wszystkie notatki" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Niestandardowe atrybuty", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Anuluj", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/pt/contact.json b/app/javascript/dashboard/i18n/locale/pt/contact.json index b167c9f50..71cd996f0 100644 --- a/app/javascript/dashboard/i18n/locale/pt/contact.json +++ b/app/javascript/dashboard/i18n/locale/pt/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Não há conversas anteriores associadas a este contato.", "TITLE": "Conversas anteriores" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Rótulos da conversa", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "cancelar", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json index 813542830..8e9936a4c 100644 --- a/app/javascript/dashboard/i18n/locale/pt_BR/contact.json +++ b/app/javascript/dashboard/i18n/locale/pt_BR/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Não há conversas anteriores associadas a este contato.", "TITLE": "Conversas anteriores" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Atributos Personalizados" - }, "LABELS": { "TITLE": "Marcador da conversa", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Atributos Personalizados", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancelar", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ro/contact.json b/app/javascript/dashboard/i18n/locale/ro/contact.json index 95c92cc76..eeadba0a3 100644 --- a/app/javascript/dashboard/i18n/locale/ro/contact.json +++ b/app/javascript/dashboard/i18n/locale/ro/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Nu există conversații anterioare asociate cu acest contact.", "TITLE": "Conversații anterioare" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Etichete conversație", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Renunță", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ru/contact.json b/app/javascript/dashboard/i18n/locale/ru/contact.json index c85c67794..e903118c7 100644 --- a/app/javascript/dashboard/i18n/locale/ru/contact.json +++ b/app/javascript/dashboard/i18n/locale/ru/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Нет предыдущих диалогов, связанных с этим контактом.", "TITLE": "Предыдущие диалоги" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Пользовательские атрибуты" - }, "LABELS": { "TITLE": "Категории Диалога", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "Показать все заметки" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Пользовательские атрибуты", + "BUTTON": "Добавить пользовательский атрибут", + "ADD": { + "TITLE": "Создать пользовательский атрибут", + "DESC": "Добавить пользовательскую информацию этому контакту." + }, + "FORM": { + "CREATE": "Добавить параметр", + "CANCEL": "Отменить", + "NAME": { + "LABEL": "Имя пользовательского параметра", + "PLACEHOLDER": "Например: id shopify", + "ERROR": "Неверное название пользовательского параметра" + }, + "VALUE": { + "LABEL": "Значение параметра", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ru/report.json b/app/javascript/dashboard/i18n/locale/ru/report.json index 9ab726c09..6851601c0 100644 --- a/app/javascript/dashboard/i18n/locale/ru/report.json +++ b/app/javascript/dashboard/i18n/locale/ru/report.json @@ -41,15 +41,15 @@ }, { "id": 2, - "name": "Last 3 months" + "name": "Последние 3 месяца" }, { "id": 3, - "name": "Last 6 months" + "name": "Последние 6 месяцев" }, { "id": 4, - "name": "Last year" + "name": "За прошлый год" } ] } diff --git a/app/javascript/dashboard/i18n/locale/sk/contact.json b/app/javascript/dashboard/i18n/locale/sk/contact.json index 6a94011a5..80a33f25a 100644 --- a/app/javascript/dashboard/i18n/locale/sk/contact.json +++ b/app/javascript/dashboard/i18n/locale/sk/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "There are no previous conversations associated to this contact.", "TITLE": "Previous Conversations" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Conversation Labels", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Cancel", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/sv/contact.json b/app/javascript/dashboard/i18n/locale/sv/contact.json index e522d6958..bea85ece1 100644 --- a/app/javascript/dashboard/i18n/locale/sv/contact.json +++ b/app/javascript/dashboard/i18n/locale/sv/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Det finns inga tidigare konversationer kopplade till denna kontakt.", "TITLE": "Tidigare konversationer" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Egna egenskaper" - }, "LABELS": { "TITLE": "Etiketter för konversation", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Egna egenskaper", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Avbryt", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/ta/contact.json b/app/javascript/dashboard/i18n/locale/ta/contact.json index 567af1644..dd14b68e9 100644 --- a/app/javascript/dashboard/i18n/locale/ta/contact.json +++ b/app/javascript/dashboard/i18n/locale/ta/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "இந்த தொடர்புடன் முந்தைய உரையாடல்கள் எதுவும் இல்லை.", "TITLE": "முந்தைய உரையாடல்கள்" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "உரையாடல் லேபிள்கள்", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "ரத்துசெய்", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/th/campaign.json b/app/javascript/dashboard/i18n/locale/th/campaign.json index 2550d305c..68e699fcf 100644 --- a/app/javascript/dashboard/i18n/locale/th/campaign.json +++ b/app/javascript/dashboard/i18n/locale/th/campaign.json @@ -1,69 +1,69 @@ { "CAMPAIGN": { - "HEADER": "Campaigns", - "SIDEBAR_TXT": "Proactive messages allow the customer to send outbound messages to their contacts which would trigger more conversations. Click on Add Campaign to create a new campaign. You can also edit or delete an existing campaign by clicking on the Edit or Delete button.", - "HEADER_BTN_TXT": "Create a campaign", + "HEADER": "แคมเปญ", + "SIDEBAR_TXT": "ข้อความที่ช่วยให้ลูกค้าสามารถส่งข้อควาไปยังผู้ติดต่อซึ่งจะทำให้เกิดการสนทนามากขึ้น คลิกที่ เพิ่มแคมเปญ เพื่อสร้างแคมเปญใหม่ คุณยังสามารถแก้ไขหรือลบแคมเปญที่มีอยู่ได้โดยคลิกที่ปุ่มแก้ไขหรือลบ", + "HEADER_BTN_TXT": "สร้างแคมเปญใหม่", "ADD": { - "TITLE": "Create a campaign", - "DESC": "Proactive messages allow the customer to send outbound messages to their contacts which would trigger more conversations.", + "TITLE": "สร้างแคมเปญใหม่", + "DESC": "ข้อความที่จะทำให้ลูกค้าส่งไปยังผู้ติดต่อที่จะได้รับข้อความมากขึ้น", "CANCEL_BUTTON_TEXT": "ยกเลิก", - "CREATE_BUTTON_TEXT": "Create", + "CREATE_BUTTON_TEXT": "สร้าง", "FORM": { "TITLE": { - "LABEL": "Title", - "PLACEHOLDER": "Please enter the title of campaign", - "ERROR": "Title is required" + "LABEL": "หัวข้อ", + "PLACEHOLDER": "กรุณาใส่หัวข้อแคมเปญ", + "ERROR": "โปรดระบุหัวข้อด้วย" }, "MESSAGE": { - "LABEL": "Message", - "PLACEHOLDER": "Please enter the message of campaign", - "ERROR": "Message is required" + "LABEL": "ข้อความ", + "PLACEHOLDER": "โปรดระบุข้อความของแคมเปญด้วย", + "ERROR": "โปรดระบุข้อความด้วย" }, "SENT_BY": { - "LABEL": "Sent by", - "PLACEHOLDER": "Please select the the content of campaign", - "ERROR": "Sender is required" + "LABEL": "ส่งโดย", + "PLACEHOLDER": "กรุณาเลือกเนื้อหาด้วย", + "ERROR": "โปรดระบุชื่อผู้ส่ง" }, "END_POINT": { - "LABEL": "URL", - "PLACEHOLDER": "Please enter the URL", - "ERROR": "Please enter a valid URL" + "LABEL": "ลิ้ง", + "PLACEHOLDER": "โปรดระบุลิ้งด้วย", + "ERROR": "กรุณากรอกลิ้งที่ถูกต้อง" }, "TIME_ON_PAGE": { - "LABEL": "Time on page(Seconds)", - "PLACEHOLDER": "Please enter the time", - "ERROR": "Time on page is required" + "LABEL": "เวลาของหน้า (วินาที)", + "PLACEHOLDER": "โปรดระบุเวลาด้วย", + "ERROR": "กรุณาระบุเวลาที่อยู่ในหน้านั้นด้วย" }, - "ENABLED": "Enable campaign", - "SUBMIT": "Add Campaign" + "ENABLED": "เปิดเเคมเปญ", + "SUBMIT": "เพิ่มเเคมเปญ" }, "API": { - "SUCCESS_MESSAGE": "Campaign created successfully", - "ERROR_MESSAGE": "There was an error. Please try again." + "SUCCESS_MESSAGE": "สร้างเเคมเปญเสร็จสิ้น", + "ERROR_MESSAGE": "เกิดข้อผิดพลาดกรุณาลองใหม่อีกครั้ง." } }, "EDIT": { - "TITLE": "Edit campaign", - "UPDATE_BUTTON_TEXT": "Update", + "TITLE": "เเก้ไขเเคมเปญ", + "UPDATE_BUTTON_TEXT": "อัพเดท", "API": { - "SUCCESS_MESSAGE": "Campaign updated successfully", + "SUCCESS_MESSAGE": "อัพเดทเเคมเปญเรียบร้อยเเล้ว", "ERROR_MESSAGE": "เกิดข้อผิดพลาดกรุณาลองใหม่อีกครั้ง" } }, "LIST": { - "LOADING_MESSAGE": "Loading campaigns...", - "404": "There are no campaigns created for this inbox.", + "LOADING_MESSAGE": "กำลังโหลดแคมเปญ", + "404": "ไม่มีเเคมเปณลญในกล่องข้อความนี้", "TABLE_HEADER": { - "TITLE": "Title", - "MESSAGE": "Message", + "TITLE": "หัวข้อ", + "MESSAGE": "ข้อความ", "STATUS": "สถานะ", - "SENDER": "Sender", - "URL": "URL", - "TIME_ON_PAGE": "Time(Seconds)", - "CREATED_AT": "Created at" + "SENDER": "ผู้ส่ง", + "URL": "ลิ้ง", + "TIME_ON_PAGE": "เวลา (วินาที)", + "CREATED_AT": "สร้างเมื่อ" }, "BUTTONS": { - "ADD": "Add", + "ADD": "เพิ่ม", "EDIT": "เเก้ไข", "DELETE": "ลบ" }, diff --git a/app/javascript/dashboard/i18n/locale/th/chatlist.json b/app/javascript/dashboard/i18n/locale/th/chatlist.json index 914b66ada..e62c9bb0f 100644 --- a/app/javascript/dashboard/i18n/locale/th/chatlist.json +++ b/app/javascript/dashboard/i18n/locale/th/chatlist.json @@ -80,7 +80,7 @@ "RECEIVED_VIA_EMAIL": "ได้รับทางอีเมลล์", "VIEW_TWEET_IN_TWITTER": "ดูทวิตในทวิตเตอร์", "REPLY_TO_TWEET": "ตอบกลับทวิตนี้", - "NO_MESSAGES": "No Messages", - "NO_CONTENT": "No content available" + "NO_MESSAGES": "ไม่มีข้อความ", + "NO_CONTENT": "ไม่มีเนื้อหา" } } diff --git a/app/javascript/dashboard/i18n/locale/th/contact.json b/app/javascript/dashboard/i18n/locale/th/contact.json index 17044bcc6..60da05a91 100644 --- a/app/javascript/dashboard/i18n/locale/th/contact.json +++ b/app/javascript/dashboard/i18n/locale/th/contact.json @@ -3,7 +3,7 @@ "NOT_AVAILABLE": "ไม่พร้อมใช้งาน", "EMAIL_ADDRESS": "ที่อยู่อีเมล์", "PHONE_NUMBER": "หมายเลขโทรศัพท์", - "COPY_SUCCESSFUL": "Copied to clipboard successfully", + "COPY_SUCCESSFUL": "คัดลอกไปยังคริปบอร์ดเเล้ว", "COMPANY": "บริษัท", "LOCATION": "สถานที่", "CONVERSATION_TITLE": "รายละเอียดการสนทนา", @@ -11,15 +11,12 @@ "OS": "ระบบปฏิบัติการ", "INITIATED_FROM": "Initiated from", "INITIATED_AT": "Initiated at", - "IP_ADDRESS": "IP Address", - "NEW_MESSAGE": "New message", + "IP_ADDRESS": "ที่อยู่ไอพี", + "NEW_MESSAGE": "ข้อความใหม่", "CONVERSATIONS": { "NO_RECORDS_FOUND": "ไม่มีการสนทนาก่อนหน้านี้ที่เชื่อมโยงกับผู้ติดต่อนี้", "TITLE": "การสนทนาก่อนหน้า" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "แอตทริบิวต์ที่กำหนดเอง" - }, "LABELS": { "TITLE": "ป้ายกำกับการสนทนา", "MODAL": { @@ -28,22 +25,22 @@ "INACTIVE_LABELS": "ป้ายกำกับที่ใช้ได้ในบัญชีนี้", "REMOVE": "คลิกที่ X เพื่อลบป้ายกำกับ", "ADD": "คลิกที่ + เพื่อเพิ่มป้ายกำกับ", - "ADD_BUTTON": "Add Labels", + "ADD_BUTTON": "เพิ่มหัวข้อ", "UPDATE_BUTTON": "อัพเดทป้ายกำกับ", "UPDATE_ERROR": "ไม่สามารถอัพเดทป้านกำกับได้กรุณาลองใหม่" }, "NO_LABELS_TO_ADD": "ไม่มีป้ายกำกับที่กำหนดให้บัญชีนี้", "NO_AVAILABLE_LABELS": "ไม่มีป้ายกำกับที่เพิ่มให้การสนทนานี้", "LABEL_SELECT": { - "TITLE": "Add Labels", - "PLACEHOLDER": "Search labels", - "NO_RESULT": "No labels found" + "TITLE": "เพิ่มหัวข้อ", + "PLACEHOLDER": "ค้นหาหัวข้อ", + "NO_RESULT": "ไม่พบหัวข้อ" } }, "MUTE_CONTACT": "ระงับการสนทนา", - "UNMUTE_CONTACT": "Unmute Conversation", + "UNMUTE_CONTACT": "เลิกใบ้การสนทนา", "MUTED_SUCCESS": "การสนทนานี้ถูกระงับ 6 ชั่วโมง", - "UNMUTED_SUCCESS": "This conversation is unmuted", + "UNMUTED_SUCCESS": "การสนทนานี้เลิกใบ้เเล้ว", "SEND_TRANSCRIPT": "ส่งประวัติเเชท", "EDIT_LABEL": "เเก้ไข" }, @@ -53,9 +50,9 @@ "DESC": "เเห้ไขรายละเอียดผู้ติดต่อ" }, "CREATE_CONTACT": { - "BUTTON_LABEL": "New Contact", - "TITLE": "Create new contact", - "DESC": "Add basic information details about the contact." + "BUTTON_LABEL": "การติดต่อใหม่", + "TITLE": "สร้างการติดต่อใหม่", + "DESC": "เพิ่าข้อความพื้นฐานเกี่ยวกับการติดต่อนี้" }, "CONTACT_FORM": { "FORM": { @@ -79,8 +76,8 @@ "PHONE_NUMBER": { "PLACEHOLDER": "กรอกหมายเลขโทรศัพท์ผู้ติดต่อ", "LABEL": "หมายเลขโทรศัพท์", - "HELP": "Phone number should be of E.164 format eg: +1415555555 [+][country code][area code][local phone number]", - "ERROR": "Phone number should be either empty or of E.164 format" + "HELP": "เบอร์โทรศัพท์ควรจะเป็นเเบบนี้เช่น +663990214 [+][หรัสประเทศ][หรัสพื้นที่][เลขโทรศัพท์]", + "ERROR": "หมายเลขโทรศัพท์ควรจะว่างปล่าวหรืออยู่ในรู้เเบบ E.164" }, "LOCATION": { "PLACEHOLDER": "กรอกที่อยู่ผู้ติดต่อ", @@ -109,66 +106,87 @@ } } }, - "SUCCESS_MESSAGE": "Contact saved successfully", + "SUCCESS_MESSAGE": "สร้างการติดต่อเรียบร้อยเเล้ว", "CONTACT_ALREADY_EXIST": "อีเมล์นี้ถูกใช้ในผู้ติดต่อคนอื่นเเล้ว", "ERROR_MESSAGE": "เกิดข้อผิดพลาดกรุณาลองใหม่อีกครั้ง" }, "NEW_CONVERSATION": { - "BUTTON_LABEL": "Start conversation", - "TITLE": "New conversation", - "DESC": "Start a new conversation by sending a new message.", - "NO_INBOX": "Couldn't find an inbox to initiate a new conversation with this contact.", + "BUTTON_LABEL": "เริ่มการสนทนา", + "TITLE": "สร้างการสนทนาใหม่", + "DESC": "สร้างการสนทนาใหม่โดยการส่งข้อความ", + "NO_INBOX": "ไม่พบกล่องข้อความเพื่อเริ่มการสนทนากับผู้ติดต่อนี้ได้", "FORM": { "TO": { - "LABEL": "To" + "LABEL": "ถึง" }, "INBOX": { - "LABEL": "Inbox", - "ERROR": "Select an inbox" + "LABEL": "กล่องข้อความ", + "ERROR": "เลือกกล่องข้อความ" }, "MESSAGE": { - "LABEL": "Message", - "PLACEHOLDER": "Write your message here", - "ERROR": "Message can't be empty" + "LABEL": "ข้อความ", + "PLACEHOLDER": "เขียนข้อความของคุณที่นี่", + "ERROR": "ไม่สามารถปล่อยให่ข้อความว่างได้" }, - "SUBMIT": "Send message", + "SUBMIT": "ส่วข้อความ", "CANCEL": "ยกเลิก", - "SUCCESS_MESSAGE": "Message sent!", - "ERROR_MESSAGE": "Couldn't send! try again" + "SUCCESS_MESSAGE": "ส่งข้อความเเล้ว", + "ERROR_MESSAGE": "ไม่สามารถส่งข้อความได้กรุณาลองอีกครั้ง" } }, "CONTACTS_PAGE": { - "HEADER": "Contacts", - "SEARCH_BUTTON": "Search", - "SEARCH_INPUT_PLACEHOLDER": "Search for contacts", + "HEADER": "ผู้ติดต่อ", + "SEARCH_BUTTON": "ค้นหา", + "SEARCH_INPUT_PLACEHOLDER": "ค้อนหาผู้ติดต่อ", "LIST": { - "LOADING_MESSAGE": "Loading contacts...", - "404": "No contacts matches your search 🔍", + "LOADING_MESSAGE": "กำลังโหลดรายชื่อผู้ติดต่อ", + "404": "ไม่มีผู้ติดต่อที่ตรงกัน 🔍", "TABLE_HEADER": { "NAME": "ชื่อ", "PHONE_NUMBER": "หมายเลขโทรศัพท์", "CONVERSATIONS": "การสนทนา", - "LAST_ACTIVITY": "Last Activity", - "COUNTRY": "Country", - "CITY": "City", - "SOCIAL_PROFILES": "Social Profiles", + "LAST_ACTIVITY": "ล่าสุดเมื่อ", + "COUNTRY": "ประเทศ", + "CITY": "เมือง", + "SOCIAL_PROFILES": "โปรไฟล์โซเชียล", "COMPANY": "บริษัท", "EMAIL_ADDRESS": "ที่อยู่อีเมล์" }, - "VIEW_DETAILS": "View details" + "VIEW_DETAILS": "ดูรายละเอียด" } }, "NOTES": { "HEADER": { - "TITLE": "Notes" + "TITLE": "โน้ต" }, "ADD": { - "BUTTON": "Add", - "PLACEHOLDER": "Add a note", - "TITLE": "Shift + Enter to create a note" + "BUTTON": "เพิ่ม", + "PLACEHOLDER": "เพิ่มโน็ต", + "TITLE": "Shift + Enter เพื่อสร้างโน็ต" }, "FOOTER": { - "BUTTON": "View all notes" + "BUTTON": "ดูโน็ตทั้งหมด" + } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "แอตทริบิวต์ที่กำหนดเอง", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "ยกเลิก", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } } } } diff --git a/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json index c91e4d2e2..93ea16b9d 100644 --- a/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/th/inboxMgmt.json @@ -227,7 +227,7 @@ "SETTINGS": "Settings", "COLLABORATORS": "Collaborators", "CONFIGURATION": "Configuration", - "CAMPAIGN": "Campaigns", + "CAMPAIGN": "แคมเปญ", "PRE_CHAT_FORM": "Pre Chat Form", "BUSINESS_HOURS": "Business Hours" }, diff --git a/app/javascript/dashboard/i18n/locale/th/settings.json b/app/javascript/dashboard/i18n/locale/th/settings.json index 16ba5f97f..60b2d1c30 100644 --- a/app/javascript/dashboard/i18n/locale/th/settings.json +++ b/app/javascript/dashboard/i18n/locale/th/settings.json @@ -123,7 +123,7 @@ "SIDEBAR": { "CONVERSATIONS": "การสนทนา", "REPORTS": "Reports", - "CONTACTS": "Contacts", + "CONTACTS": "ผู้ติดต่อ", "SETTINGS": "Settings", "HOME": "Home", "AGENTS": "พนักงาน", diff --git a/app/javascript/dashboard/i18n/locale/tr/contact.json b/app/javascript/dashboard/i18n/locale/tr/contact.json index cc6055f3b..78782d064 100644 --- a/app/javascript/dashboard/i18n/locale/tr/contact.json +++ b/app/javascript/dashboard/i18n/locale/tr/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Bu kişiyle ilişkilendirilmiş önceki görüşme yok.", "TITLE": "Önceki Sohbetler" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Özel Nitelikler" - }, "LABELS": { "TITLE": "Konuşma Etiketleri", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Özel Nitelikler", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "İptal Et", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/uk/contact.json b/app/javascript/dashboard/i18n/locale/uk/contact.json index 4afbf67ff..ab9bed67e 100644 --- a/app/javascript/dashboard/i18n/locale/uk/contact.json +++ b/app/javascript/dashboard/i18n/locale/uk/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Не було попередніх бесід, пов'язаних з цим контактом.", "TITLE": "Попередні бесіди" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Custom Attributes" - }, "LABELS": { "TITLE": "Мітки бесіди", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Custom Attributes", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Скасувати", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/vi/contact.json b/app/javascript/dashboard/i18n/locale/vi/contact.json index a101e7b01..d22a350ed 100644 --- a/app/javascript/dashboard/i18n/locale/vi/contact.json +++ b/app/javascript/dashboard/i18n/locale/vi/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "Không có cuộc trò chuyện trước đó được liên kết với liên hệ này.", "TITLE": "Cuộc trò chuyện trước đó" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "Thuộc tính tùy chỉnh" - }, "LABELS": { "TITLE": "Nhãn hội thoại", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "Thuộc tính tùy chỉnh", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "Huỷ", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/zh_CN/contact.json b/app/javascript/dashboard/i18n/locale/zh_CN/contact.json index 468689530..36b463c4d 100644 --- a/app/javascript/dashboard/i18n/locale/zh_CN/contact.json +++ b/app/javascript/dashboard/i18n/locale/zh_CN/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "此联系人没有关联到以前的会话。", "TITLE": "上一次对话" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "自定义属性" - }, "LABELS": { "TITLE": "对话标记", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "View all notes" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "自定义属性", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "取消", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/dashboard/i18n/locale/zh_TW/contact.json b/app/javascript/dashboard/i18n/locale/zh_TW/contact.json index b1b4e6e43..d11179d55 100644 --- a/app/javascript/dashboard/i18n/locale/zh_TW/contact.json +++ b/app/javascript/dashboard/i18n/locale/zh_TW/contact.json @@ -17,9 +17,6 @@ "NO_RECORDS_FOUND": "此聯絡人没有關聯到以前的對話。", "TITLE": "上一次對話" }, - "CUSTOM_ATTRIBUTES": { - "TITLE": "自訂屬性" - }, "LABELS": { "TITLE": "對話標記", "MODAL": { @@ -170,5 +167,26 @@ "FOOTER": { "BUTTON": "查看所有筆記" } + }, + "CUSTOM_ATTRIBUTES": { + "TITLE": "自訂屬性", + "BUTTON": "Add custom attribute", + "ADD": { + "TITLE": "Create custom attribute", + "DESC": "Add custom information to this contact." + }, + "FORM": { + "CREATE": "Add attribute", + "CANCEL": "取消", + "NAME": { + "LABEL": "Custom attribute name", + "PLACEHOLDER": "Eg: shopify id", + "ERROR": "Invalid custom attribute name" + }, + "VALUE": { + "LABEL": "Attribute value", + "PLACEHOLDER": "Eg: 11901 " + } + } } } diff --git a/app/javascript/widget/i18n/locale/ar.json b/app/javascript/widget/i18n/locale/ar.json index 8b07ae71e..775cf0d3b 100644 --- a/app/javascript/widget/i18n/locale/ar.json +++ b/app/javascript/widget/i18n/locale/ar.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "عرض الرسائل الجديدة", "CLOSE_MESSAGES_BUTTON": "أغلق", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "رد آلي" }, "BUBBLE": { "LABEL": "تحدث الينا" diff --git a/app/javascript/widget/i18n/locale/ca.json b/app/javascript/widget/i18n/locale/ca.json index 6c9c9b1a8..31bc0eea3 100644 --- a/app/javascript/widget/i18n/locale/ca.json +++ b/app/javascript/widget/i18n/locale/ca.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Veure missatges nous", "CLOSE_MESSAGES_BUTTON": "Tanca", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Xateja amb nosaltres" diff --git a/app/javascript/widget/i18n/locale/cs.json b/app/javascript/widget/i18n/locale/cs.json index ebe3d633c..e15395cc1 100644 --- a/app/javascript/widget/i18n/locale/cs.json +++ b/app/javascript/widget/i18n/locale/cs.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Zobrazit nové zprávy", "CLOSE_MESSAGES_BUTTON": "Zavřít", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Napiště nám" diff --git a/app/javascript/widget/i18n/locale/da.json b/app/javascript/widget/i18n/locale/da.json index 8677b9d0b..0189260aa 100644 --- a/app/javascript/widget/i18n/locale/da.json +++ b/app/javascript/widget/i18n/locale/da.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Se nye beskeder", "CLOSE_MESSAGES_BUTTON": "Luk", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat med os" diff --git a/app/javascript/widget/i18n/locale/de.json b/app/javascript/widget/i18n/locale/de.json index b5a1ccff3..ea2dd7987 100644 --- a/app/javascript/widget/i18n/locale/de.json +++ b/app/javascript/widget/i18n/locale/de.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Neue Nachrichten anzeigen", "CLOSE_MESSAGES_BUTTON": "Schließen", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chatten Sie mit uns" diff --git a/app/javascript/widget/i18n/locale/el.json b/app/javascript/widget/i18n/locale/el.json index 91cf78970..bb678a457 100644 --- a/app/javascript/widget/i18n/locale/el.json +++ b/app/javascript/widget/i18n/locale/el.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Δείτε τα νέα μηνύματα", "CLOSE_MESSAGES_BUTTON": "Κλείσιμο", - "COMPANY_FROM": "from" + "COMPANY_FROM": "από", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Συνομιλήστε μαζί μας" diff --git a/app/javascript/widget/i18n/locale/es.json b/app/javascript/widget/i18n/locale/es.json index 4335ea072..2ca5d1e9b 100644 --- a/app/javascript/widget/i18n/locale/es.json +++ b/app/javascript/widget/i18n/locale/es.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Ver nuevos mensajes", "CLOSE_MESSAGES_BUTTON": "Cerrar", - "COMPANY_FROM": "De" + "COMPANY_FROM": "De", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chatea con nosotros" diff --git a/app/javascript/widget/i18n/locale/fa.json b/app/javascript/widget/i18n/locale/fa.json index 199421ab9..4b0ddd988 100644 --- a/app/javascript/widget/i18n/locale/fa.json +++ b/app/javascript/widget/i18n/locale/fa.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "مشاهده پیام‌های جدید", "CLOSE_MESSAGES_BUTTON": "بستن", - "COMPANY_FROM": "از" + "COMPANY_FROM": "از", + "BOT": "ربات" }, "BUBBLE": { "LABEL": "با ما گفتگو کنید" diff --git a/app/javascript/widget/i18n/locale/fi.json b/app/javascript/widget/i18n/locale/fi.json index cb8d9b3a9..de9bd4131 100644 --- a/app/javascript/widget/i18n/locale/fi.json +++ b/app/javascript/widget/i18n/locale/fi.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Näytä uudet viestit", "CLOSE_MESSAGES_BUTTON": "Sulje", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Botti" }, "BUBBLE": { "LABEL": "Keskustele kanssamme" diff --git a/app/javascript/widget/i18n/locale/fr.json b/app/javascript/widget/i18n/locale/fr.json index 9482b6370..0b7146c3c 100644 --- a/app/javascript/widget/i18n/locale/fr.json +++ b/app/javascript/widget/i18n/locale/fr.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Voir les nouveaux messages", "CLOSE_MESSAGES_BUTTON": "Fermer", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Discutez avec nous" diff --git a/app/javascript/widget/i18n/locale/hi.json b/app/javascript/widget/i18n/locale/hi.json index 10228d7ff..4e22e35e6 100644 --- a/app/javascript/widget/i18n/locale/hi.json +++ b/app/javascript/widget/i18n/locale/hi.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "Close", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/hu.json b/app/javascript/widget/i18n/locale/hu.json index d0f3ca792..22f124905 100644 --- a/app/javascript/widget/i18n/locale/hu.json +++ b/app/javascript/widget/i18n/locale/hu.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Új üzenetek", "CLOSE_MESSAGES_BUTTON": "Bezárás", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chatelj velünk" diff --git a/app/javascript/widget/i18n/locale/id.json b/app/javascript/widget/i18n/locale/id.json index a643b6db5..93e85d860 100644 --- a/app/javascript/widget/i18n/locale/id.json +++ b/app/javascript/widget/i18n/locale/id.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Lihat pesan baru", "CLOSE_MESSAGES_BUTTON": "Tutup", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat dengan kami" diff --git a/app/javascript/widget/i18n/locale/it.json b/app/javascript/widget/i18n/locale/it.json index b9e5b4208..3416fa1dc 100644 --- a/app/javascript/widget/i18n/locale/it.json +++ b/app/javascript/widget/i18n/locale/it.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Vedi nuovi messaggi", "CLOSE_MESSAGES_BUTTON": "Chiudi", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chatta con noi" diff --git a/app/javascript/widget/i18n/locale/ja.json b/app/javascript/widget/i18n/locale/ja.json index c477dc41c..9a6c16832 100644 --- a/app/javascript/widget/i18n/locale/ja.json +++ b/app/javascript/widget/i18n/locale/ja.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "新しいメッセージを見る", "CLOSE_MESSAGES_BUTTON": "閉じる", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "チャットをする" diff --git a/app/javascript/widget/i18n/locale/ko.json b/app/javascript/widget/i18n/locale/ko.json index 4b0cd8d7a..19e31da12 100644 --- a/app/javascript/widget/i18n/locale/ko.json +++ b/app/javascript/widget/i18n/locale/ko.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "새 메시지 보기", "CLOSE_MESSAGES_BUTTON": "닫기", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "봇" }, "BUBBLE": { "LABEL": "채팅하기" diff --git a/app/javascript/widget/i18n/locale/ml.json b/app/javascript/widget/i18n/locale/ml.json index 283491f72..a049fc670 100644 --- a/app/javascript/widget/i18n/locale/ml.json +++ b/app/javascript/widget/i18n/locale/ml.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "പുതിയ സന്ദേശങ്ങൾ കാണുക", "CLOSE_MESSAGES_BUTTON": "അടയ്ക്കുക", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "ബോട്ട്" }, "BUBBLE": { "LABEL": "ഞങ്ങളുമായി ചാറ്റുചെയ്യുക" diff --git a/app/javascript/widget/i18n/locale/ne.json b/app/javascript/widget/i18n/locale/ne.json index aed47cc34..541e859c3 100644 --- a/app/javascript/widget/i18n/locale/ne.json +++ b/app/javascript/widget/i18n/locale/ne.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "नयाँ सन्देशहरू हेर्नुहोस्", "CLOSE_MESSAGES_BUTTON": "बन्दा गार्नुहोस्", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "हामीसँग कुराकानी गर्नुहोस्" diff --git a/app/javascript/widget/i18n/locale/nl.json b/app/javascript/widget/i18n/locale/nl.json index b12a8c143..d2934d7c7 100644 --- a/app/javascript/widget/i18n/locale/nl.json +++ b/app/javascript/widget/i18n/locale/nl.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Bekijk nieuwe berichten", "CLOSE_MESSAGES_BUTTON": "Sluiten", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat met ons" diff --git a/app/javascript/widget/i18n/locale/no.json b/app/javascript/widget/i18n/locale/no.json index 6f01f2f48..9454bafc5 100644 --- a/app/javascript/widget/i18n/locale/no.json +++ b/app/javascript/widget/i18n/locale/no.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Se nye meldinger", "CLOSE_MESSAGES_BUTTON": "Lukk", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat med oss" diff --git a/app/javascript/widget/i18n/locale/pl.json b/app/javascript/widget/i18n/locale/pl.json index fdd1302e1..22a2f6bd6 100644 --- a/app/javascript/widget/i18n/locale/pl.json +++ b/app/javascript/widget/i18n/locale/pl.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Pokaż nowe wiadomości", "CLOSE_MESSAGES_BUTTON": "Zamknij", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Porozmawiaj z nami" diff --git a/app/javascript/widget/i18n/locale/pt.json b/app/javascript/widget/i18n/locale/pt.json index 542b20b3a..e6cc0dd70 100644 --- a/app/javascript/widget/i18n/locale/pt.json +++ b/app/javascript/widget/i18n/locale/pt.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "FECHAR", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/pt_BR.json b/app/javascript/widget/i18n/locale/pt_BR.json index 3e6d9ecc4..404f75f3b 100644 --- a/app/javascript/widget/i18n/locale/pt_BR.json +++ b/app/javascript/widget/i18n/locale/pt_BR.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Ver novas mensagens", "CLOSE_MESSAGES_BUTTON": "Fechar", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Fale conosco no chat" diff --git a/app/javascript/widget/i18n/locale/ro.json b/app/javascript/widget/i18n/locale/ro.json index 18270dd14..f64451399 100644 --- a/app/javascript/widget/i18n/locale/ro.json +++ b/app/javascript/widget/i18n/locale/ro.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Vezi mesajele noi", "CLOSE_MESSAGES_BUTTON": "Închide", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/ru.json b/app/javascript/widget/i18n/locale/ru.json index 0e8255755..4c91a2783 100644 --- a/app/javascript/widget/i18n/locale/ru.json +++ b/app/javascript/widget/i18n/locale/ru.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Посмотреть новые сообщения", "CLOSE_MESSAGES_BUTTON": "Закрыть", - "COMPANY_FROM": "from" + "COMPANY_FROM": "от", + "BOT": "Бот" }, "BUBBLE": { "LABEL": "Пообщайтесь с нами" diff --git a/app/javascript/widget/i18n/locale/sk.json b/app/javascript/widget/i18n/locale/sk.json index 10228d7ff..4e22e35e6 100644 --- a/app/javascript/widget/i18n/locale/sk.json +++ b/app/javascript/widget/i18n/locale/sk.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "Close", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/sv.json b/app/javascript/widget/i18n/locale/sv.json index 13b29267a..2947e8db0 100644 --- a/app/javascript/widget/i18n/locale/sv.json +++ b/app/javascript/widget/i18n/locale/sv.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Se nya meddelanden", "CLOSE_MESSAGES_BUTTON": "Stäng", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chatta med oss" diff --git a/app/javascript/widget/i18n/locale/ta.json b/app/javascript/widget/i18n/locale/ta.json index 2b623f3e6..0a9527cd7 100644 --- a/app/javascript/widget/i18n/locale/ta.json +++ b/app/javascript/widget/i18n/locale/ta.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "மூடு", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/th.json b/app/javascript/widget/i18n/locale/th.json index 3b0de7943..87c601143 100644 --- a/app/javascript/widget/i18n/locale/th.json +++ b/app/javascript/widget/i18n/locale/th.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "ปิด", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "บอท" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/tr.json b/app/javascript/widget/i18n/locale/tr.json index fda4fe17b..3088e332d 100644 --- a/app/javascript/widget/i18n/locale/tr.json +++ b/app/javascript/widget/i18n/locale/tr.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Yeni mesajları gör", "CLOSE_MESSAGES_BUTTON": "Kapat", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Sohbet et" diff --git a/app/javascript/widget/i18n/locale/uk.json b/app/javascript/widget/i18n/locale/uk.json index ea9c47586..328977c54 100644 --- a/app/javascript/widget/i18n/locale/uk.json +++ b/app/javascript/widget/i18n/locale/uk.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "Закрити", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Chat with us" diff --git a/app/javascript/widget/i18n/locale/vi.json b/app/javascript/widget/i18n/locale/vi.json index 50164b1bf..95bcea754 100644 --- a/app/javascript/widget/i18n/locale/vi.json +++ b/app/javascript/widget/i18n/locale/vi.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "Xem tin nhắn mới", "CLOSE_MESSAGES_BUTTON": "Đóng", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "Bot" }, "BUBBLE": { "LABEL": "Trò chuyện với chúng tôi" diff --git a/app/javascript/widget/i18n/locale/zh_CN.json b/app/javascript/widget/i18n/locale/zh_CN.json index fb6c6ddbd..61018995a 100644 --- a/app/javascript/widget/i18n/locale/zh_CN.json +++ b/app/javascript/widget/i18n/locale/zh_CN.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "查看新消息", "CLOSE_MESSAGES_BUTTON": "关闭", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "机器人" }, "BUBBLE": { "LABEL": "联系我们" diff --git a/app/javascript/widget/i18n/locale/zh_TW.json b/app/javascript/widget/i18n/locale/zh_TW.json index ea4945a60..21e78e4e6 100644 --- a/app/javascript/widget/i18n/locale/zh_TW.json +++ b/app/javascript/widget/i18n/locale/zh_TW.json @@ -21,7 +21,8 @@ "UNREAD_VIEW": { "VIEW_MESSAGES_BUTTON": "See new messages", "CLOSE_MESSAGES_BUTTON": "關閉", - "COMPANY_FROM": "from" + "COMPANY_FROM": "from", + "BOT": "機器人" }, "BUBBLE": { "LABEL": "與我們對話" From 62d19911bead15c20e7b7a12f58ca8f5fea2ce34 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Mon, 24 May 2021 12:43:03 +0530 Subject: [PATCH 12/67] chore: Add chatwoot utils package (#2307) * add chatwoot utils package * remove color helper * replace contrasting text color with chatwoot util --- app/javascript/dashboard/components/ui/Label.vue | 2 +- app/javascript/shared/helpers/ColorHelper.js | 8 -------- app/javascript/widget/components/PreChat/Form.vue | 2 +- app/javascript/widget/components/TeamAvailability.vue | 2 +- package.json | 1 + yarn.lock | 5 +++++ 6 files changed, 9 insertions(+), 11 deletions(-) delete mode 100644 app/javascript/shared/helpers/ColorHelper.js diff --git a/app/javascript/dashboard/components/ui/Label.vue b/app/javascript/dashboard/components/ui/Label.vue index a6c09e562..14376d530 100644 --- a/app/javascript/dashboard/components/ui/Label.vue +++ b/app/javascript/dashboard/components/ui/Label.vue @@ -7,7 +7,7 @@ From 3b39eb3e33894b0ac0884b14745815864da673ad Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 27 May 2021 20:07:21 +0530 Subject: [PATCH 18/67] chore: ability to run Chatwoot as API only server (#2344) - Ability to run chatwoot as an API only server - Removes action cable testing gem since it is merged to rails 6 --- .env.example | 5 +++++ Gemfile | 2 -- Gemfile.lock | 3 --- config/environments/development.rb | 9 +++++++++ config/environments/production.rb | 4 ++++ config/routes.rb | 17 +++++++++++------ spec/controllers/dashboard_controller_spec.rb | 14 ++++++++++++++ spec/rails_helper.rb | 1 + 8 files changed, 44 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 624a44a7b..8d03180a8 100644 --- a/.env.example +++ b/.env.example @@ -143,6 +143,11 @@ USE_INBOX_AVATAR_FOR_BOT=true # maxmindb api key to use geoip2 service # IP_LOOKUP_API_KEY= + +## Running chatwoot as an API only server +## setting this value to true will disable the frontend dashboard endpoints +# CW_API_ONLY_SERVER=false + ## Development Only Config # if you want to use letter_opener for local emails # LETTER_OPENER=true diff --git a/Gemfile b/Gemfile index 4e430a7c8..c4fa85bc5 100644 --- a/Gemfile +++ b/Gemfile @@ -132,8 +132,6 @@ group :test do end group :development, :test do - # locking until https://github.com/codeclimate/test-reporter/issues/418 is resolved - gem 'action-cable-testing' gem 'bundle-audit', require: false gem 'byebug', platform: :mri gem 'factory_bot_rails' diff --git a/Gemfile.lock b/Gemfile.lock index abb305465..e372d1545 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,8 +16,6 @@ GIT GEM remote: https://rubygems.org/ specs: - action-cable-testing (0.6.1) - actioncable (>= 5.0) actioncable (6.0.3.7) actionpack (= 6.0.3.7) nio4r (~> 2.0) @@ -613,7 +611,6 @@ PLATFORMS ruby DEPENDENCIES - action-cable-testing activerecord-import acts-as-taggable-on administrate diff --git a/config/environments/development.rb b/config/environments/development.rb index 557000065..a03f8f2b4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -76,4 +76,13 @@ Rails.application.configure do Bullet.bullet_logger = true Bullet.rails_logger = true end + + # ref: https://github.com/cyu/rack-cors + config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + resource '/packs/*', headers: :any, methods: [:get, :options] + resource '*', headers: :any, methods: :any, expose: ['access-token', 'client', 'uid', 'expiry'] + end + end end diff --git a/config/environments/production.rb b/config/environments/production.rb index f30ee2239..c812e7d10 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -110,10 +110,14 @@ Rails.application.configure do # font cors issue with CDN # Ref: https://stackoverflow.com/questions/56960709/rails-font-cors-policy + # ref: https://github.com/cyu/rack-cors config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '/packs/*', headers: :any, methods: [:get, :options] + if ActiveModel::Type::Boolean.new.cast(ENV.fetch('CW_API_ONLY_SERVER', false)) + resource '*', headers: :any, methods: :any, expose: ['access-token', 'client', 'uid', 'expiry'] + end end end end diff --git a/config/routes.rb b/config/routes.rb index 140e19400..bbfe3599b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,14 +8,19 @@ Rails.application.routes.draw do token_validations: 'devise_overrides/token_validations' }, via: [:get, :post] - root to: 'dashboard#index' + ## renders the frontend paths only if its not an api only server + if ActiveModel::Type::Boolean.new.cast(ENV.fetch('CW_API_ONLY_SERVER', false)) + root to: 'api#index' + else + root to: 'dashboard#index' - get '/app', to: 'dashboard#index' - get '/app/*params', to: 'dashboard#index' - get '/app/accounts/:account_id/settings/inboxes/new/twitter', to: 'dashboard#index', as: 'app_new_twitter_inbox' - get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_twitter_inbox_agents' + get '/app', to: 'dashboard#index' + get '/app/*params', to: 'dashboard#index' + get '/app/accounts/:account_id/settings/inboxes/new/twitter', to: 'dashboard#index', as: 'app_new_twitter_inbox' + get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_twitter_inbox_agents' - resource :widget, only: [:show] + resource :widget, only: [:show] + end get '/api', to: 'api#index' namespace :api, defaults: { format: 'json' } do diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 7ada22702..e797c878a 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -17,4 +17,18 @@ describe '/app/login', type: :request do ENV['DEFAULT_LOCALE'] = 'en' end end + + # Routes are loaded once on app start + # hence Rails.application.reload_routes! is used in this spec + # ref : https://stackoverflow.com/a/63584877/939299 + context 'with CW_API_ONLY_SERVER true' do + it 'returns 404' do + ENV['CW_API_ONLY_SERVER'] = 'true' + Rails.application.reload_routes! + get '/app/login' + expect(response).to have_http_status(:not_found) + ENV['CW_API_ONLY_SERVER'] = nil + Rails.application.reload_routes! + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index b219a337c..4c8b38e25 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -66,6 +66,7 @@ RSpec.configure do |config| config.include SlackStubs config.include Devise::Test::IntegrationHelpers, type: :request config.include ActiveSupport::Testing::TimeHelpers + config.include ActionCable::TestHelper end Shoulda::Matchers.configure do |config| From 25ba852b6852b99aca800d3a40bddce3e499dbef Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Fri, 28 May 2021 19:21:16 +0530 Subject: [PATCH 19/67] chore: Adjust Chatwoot Config to deploy frontend as a separate app (#2347) --- .../dashboard/assets/scss/_woot.scss | 1 - .../components/widgets/InboxListItem.vue | 16 ++++---- .../components/widgets/WootWriter/Editor.vue | 1 + app/javascript/dashboard/constants.js | 5 +-- app/javascript/dashboard/helper/APIHelper.js | 9 ++--- .../dashboard/helper/actionCable.js | 3 +- .../dashboard/routes/auth/Confirmation.vue | 38 ++++++++++--------- .../dashboard/routes/auth/PasswordEdit.vue | 7 ++-- .../dashboard/routes/auth/ResetPassword.vue | 2 - .../dashboard/routes/auth/Signup.vue | 3 +- .../dashboard/settings/canned/EditCanned.vue | 1 - .../dashboard/settings/integrations/New.vue | 1 - app/javascript/dashboard/routes/index.js | 14 +++---- app/javascript/dashboard/routes/index.spec.js | 4 -- .../dashboard/store/modules/auth.js | 3 +- .../helpers/BaseActionCableConnector.js | 5 ++- 16 files changed, 53 insertions(+), 60 deletions(-) diff --git a/app/javascript/dashboard/assets/scss/_woot.scss b/app/javascript/dashboard/assets/scss/_woot.scss index 7ecec198b..1712e256e 100644 --- a/app/javascript/dashboard/assets/scss/_woot.scss +++ b/app/javascript/dashboard/assets/scss/_woot.scss @@ -46,5 +46,4 @@ @import 'plugins/multiselect'; @import 'plugins/dropdown'; -@import '@chatwoot/prosemirror-schema/src/woot-editor.css'; @import '~shared/assets/stylesheets/ionicons'; diff --git a/app/javascript/dashboard/components/widgets/InboxListItem.vue b/app/javascript/dashboard/components/widgets/InboxListItem.vue index 413fec00c..5bfdacdda 100644 --- a/app/javascript/dashboard/components/widgets/InboxListItem.vue +++ b/app/javascript/dashboard/components/widgets/InboxListItem.vue @@ -1,21 +1,23 @@ diff --git a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue index bbb1d4e82..ba26ef177 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue @@ -28,6 +28,7 @@ import TagAgents from '../conversation/TagAgents.vue'; import { EditorState } from 'prosemirror-state'; import { defaultMarkdownParser } from 'prosemirror-markdown'; import { wootWriterSetup } from '@chatwoot/prosemirror-schema'; +import '@chatwoot/prosemirror-schema/src/woot-editor.css'; const createState = (content, placeholder, plugins = []) => { return EditorState.create({ diff --git a/app/javascript/dashboard/constants.js b/app/javascript/dashboard/constants.js index 9a00775bf..34f4b9845 100644 --- a/app/javascript/dashboard/constants.js +++ b/app/javascript/dashboard/constants.js @@ -1,8 +1,4 @@ export default { - APP_BASE_URL: '/', - get apiURL() { - return `${this.APP_BASE_URL}/`; - }, GRAVATAR_URL: 'https://www.gravatar.com/avatar/', ASSIGNEE_TYPE: { ME: 'me', @@ -15,3 +11,4 @@ export default { BOT: 'bot', }, }; +export const DEFAULT_REDIRECT_URL = '/app/'; diff --git a/app/javascript/dashboard/helper/APIHelper.js b/app/javascript/dashboard/helper/APIHelper.js index 03e902259..5dcb34f97 100644 --- a/app/javascript/dashboard/helper/APIHelper.js +++ b/app/javascript/dashboard/helper/APIHelper.js @@ -1,14 +1,11 @@ /* eslint no-console: 0 */ -import constants from '../constants'; import Auth from '../api/auth'; -const parseErrorCode = error => { - return Promise.reject(error); -}; +const parseErrorCode = error => Promise.reject(error); export default axios => { - const wootApi = axios.create(); - wootApi.defaults.baseURL = constants.apiURL; + const { apiHost = '' } = window.chatwootConfig || {}; + const wootApi = axios.create({ baseURL: `${apiHost}/` }); // Add Auth Headers to requests if logged in if (Auth.isLoggedIn()) { const { diff --git a/app/javascript/dashboard/helper/actionCable.js b/app/javascript/dashboard/helper/actionCable.js index 6103ee915..afddb1e88 100644 --- a/app/javascript/dashboard/helper/actionCable.js +++ b/app/javascript/dashboard/helper/actionCable.js @@ -4,7 +4,8 @@ import { newMessageNotification } from 'shared/helpers/AudioNotificationHelper'; class ActionCableConnector extends BaseActionCableConnector { constructor(app, pubsubToken) { - super(app, pubsubToken); + const { websocketURL = '' } = window.chatwootConfig || {}; + super(app, pubsubToken, websocketURL); this.CancelTyping = []; this.events = { 'message.created': this.onMessageCreated, diff --git a/app/javascript/dashboard/routes/auth/Confirmation.vue b/app/javascript/dashboard/routes/auth/Confirmation.vue index ff41375a6..db439af03 100644 --- a/app/javascript/dashboard/routes/auth/Confirmation.vue +++ b/app/javascript/dashboard/routes/auth/Confirmation.vue @@ -2,31 +2,35 @@ diff --git a/app/javascript/dashboard/routes/auth/PasswordEdit.vue b/app/javascript/dashboard/routes/auth/PasswordEdit.vue index 698c3f792..4549528a0 100644 --- a/app/javascript/dashboard/routes/auth/PasswordEdit.vue +++ b/app/javascript/dashboard/routes/auth/PasswordEdit.vue @@ -46,12 +46,11 @@ + + From 9d16e52e333ec1aba5347e1691ca121d7712dd47 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 31 May 2021 10:58:43 +0530 Subject: [PATCH 21/67] chore: Add Swagger docs for Platform APIs (#2349) --- swagger/definitions/index.yml | 11 + .../request/account/create_update_payload.yml | 5 + .../agent_bot/create_update_payload.yml | 11 + .../request/user/create_update_payload.yml | 12 + swagger/definitions/resource/agent_bot.yml | 9 +- .../definitions/resource/platform_account.yml | 9 + swagger/index.yml | 28 +- swagger/parameters/agent_bot_id.yml | 6 + swagger/parameters/index.yml | 3 + swagger/paths/agent_bots/create.yml | 19 + swagger/paths/agent_bots/delete.yml | 12 + swagger/paths/agent_bots/index.yml | 33 +- swagger/paths/agent_bots/show.yml | 14 + swagger/paths/agent_bots/update.yml | 18 + swagger/paths/inboxes/get_agent_bot.yml | 21 + swagger/paths/inboxes/index.yml | 3 +- swagger/paths/index.yml | 220 +- swagger/paths/integrations/apps/show.yml | 5 +- .../paths/platform/account_users/create.yml | 40 + .../paths/platform/account_users/delete.yml | 26 + .../paths/platform/account_users/index.yml | 28 + swagger/paths/platform/accounts/create.yml | 20 + swagger/paths/platform/accounts/delete.yml | 14 + swagger/paths/platform/accounts/show.yml | 16 + swagger/paths/platform/accounts/update.yml | 20 + swagger/paths/platform/agent_bots/create.yml | 20 + swagger/paths/platform/agent_bots/delete.yml | 14 + swagger/paths/platform/agent_bots/index.yml | 17 + swagger/paths/platform/agent_bots/show.yml | 16 + swagger/paths/platform/agent_bots/update.yml | 20 + swagger/paths/platform/users/create.yml | 20 + swagger/paths/platform/users/delete.yml | 14 + swagger/paths/platform/users/login.yml | 21 + swagger/paths/platform/users/show.yml | 16 + swagger/paths/platform/users/update.yml | 20 + swagger/paths/teams/index.yml | 3 +- swagger/swagger.json | 2368 +++++++++++------ 37 files changed, 2315 insertions(+), 837 deletions(-) create mode 100644 swagger/definitions/request/account/create_update_payload.yml create mode 100644 swagger/definitions/request/agent_bot/create_update_payload.yml create mode 100644 swagger/definitions/request/user/create_update_payload.yml create mode 100644 swagger/definitions/resource/platform_account.yml create mode 100644 swagger/parameters/agent_bot_id.yml create mode 100644 swagger/paths/agent_bots/create.yml create mode 100644 swagger/paths/agent_bots/delete.yml create mode 100644 swagger/paths/agent_bots/show.yml create mode 100644 swagger/paths/agent_bots/update.yml create mode 100644 swagger/paths/inboxes/get_agent_bot.yml create mode 100644 swagger/paths/platform/account_users/create.yml create mode 100644 swagger/paths/platform/account_users/delete.yml create mode 100644 swagger/paths/platform/account_users/index.yml create mode 100644 swagger/paths/platform/accounts/create.yml create mode 100644 swagger/paths/platform/accounts/delete.yml create mode 100644 swagger/paths/platform/accounts/show.yml create mode 100644 swagger/paths/platform/accounts/update.yml create mode 100644 swagger/paths/platform/agent_bots/create.yml create mode 100644 swagger/paths/platform/agent_bots/delete.yml create mode 100644 swagger/paths/platform/agent_bots/index.yml create mode 100644 swagger/paths/platform/agent_bots/show.yml create mode 100644 swagger/paths/platform/agent_bots/update.yml create mode 100644 swagger/paths/platform/users/create.yml create mode 100644 swagger/paths/platform/users/delete.yml create mode 100644 swagger/paths/platform/users/login.yml create mode 100644 swagger/paths/platform/users/show.yml create mode 100644 swagger/paths/platform/users/update.yml diff --git a/swagger/definitions/index.yml b/swagger/definitions/index.yml index 7317ff2b0..14b71bb5b 100644 --- a/swagger/definitions/index.yml +++ b/swagger/definitions/index.yml @@ -25,6 +25,8 @@ contactable_inboxes: $ref: ./resource/contactable_inboxes.yml account: $ref: ./resource/account.yml +platform_account: + $ref: ./resource/platform_account.yml team: $ref: ./resource/team.yml integrations_app: @@ -78,6 +80,15 @@ extended_message: # REQUEST +account_create_update_payload: + $ref: ./request/account/create_update_payload.yml + +agent_bot_create_update_payload: + $ref: ./request/agent_bot/create_update_payload.yml + +user_create_update_payload: + $ref: ./request/account/create_update_payload.yml + ## contact contact_create: $ref: ./request/contact/create.yml diff --git a/swagger/definitions/request/account/create_update_payload.yml b/swagger/definitions/request/account/create_update_payload.yml new file mode 100644 index 000000000..63c0eed13 --- /dev/null +++ b/swagger/definitions/request/account/create_update_payload.yml @@ -0,0 +1,5 @@ +type: object +properties: + name: + type: string + description: Name of the account diff --git a/swagger/definitions/request/agent_bot/create_update_payload.yml b/swagger/definitions/request/agent_bot/create_update_payload.yml new file mode 100644 index 000000000..4dc74bea3 --- /dev/null +++ b/swagger/definitions/request/agent_bot/create_update_payload.yml @@ -0,0 +1,11 @@ +type: object +properties: + name: + type: string + description: The name of the agent bot + description: + type: string + description: The description about the agent bot + outgoing_url: + type: string + description: The webhook URL for the bot diff --git a/swagger/definitions/request/user/create_update_payload.yml b/swagger/definitions/request/user/create_update_payload.yml new file mode 100644 index 000000000..a7745f18f --- /dev/null +++ b/swagger/definitions/request/user/create_update_payload.yml @@ -0,0 +1,12 @@ +type: object +properties: + name: + type: string + description: Name of the user + email: + type: string + description: Email of the user + password: + type: string + description: Password must contain uppercase, lowercase letters, number and a special character + diff --git a/swagger/definitions/resource/agent_bot.yml b/swagger/definitions/resource/agent_bot.yml index d0f0efb3e..d94777d37 100644 --- a/swagger/definitions/resource/agent_bot.yml +++ b/swagger/definitions/resource/agent_bot.yml @@ -3,12 +3,15 @@ properties: id: type: number description: ID of the agent bot - description: - type: string - description: The description about the agent bot name: type: string description: The name of the agent bot + description: + type: string + description: The description about the agent bot + account_id: + type: number + description: Account ID if it's an account specific bot outgoing_url: type: string description: The webhook URL for the bot diff --git a/swagger/definitions/resource/platform_account.yml b/swagger/definitions/resource/platform_account.yml new file mode 100644 index 000000000..39399ebd2 --- /dev/null +++ b/swagger/definitions/resource/platform_account.yml @@ -0,0 +1,9 @@ +type: object +properties: + id: + type: number + description: Account ID + name: + type: string + description: Name of the account + diff --git a/swagger/index.yml b/swagger/index.yml index dc9cbedb9..0719c0739 100644 --- a/swagger/index.yml +++ b/swagger/index.yml @@ -5,12 +5,12 @@ info: title: Chatwoot termsOfService: https://www.chatwoot.com/terms-of-service/ contact: - email: support@chatwoot.com + email: hello@chatwoot.com license: name: MIT License url: https://opensource.org/licenses/MIT host: app.chatwoot.com -basePath: /api/v1/ +basePath: schemes: - https produces: @@ -28,11 +28,11 @@ securityDefinitions: in: header name: api_access_token description: This token should be provided by system admin or obtained via rails console. This token can be used to build bot integrations and can only access limited apis. - superAdminApiKey: + platformAppApiKey: type: apiKey in: header name: api_access_token - description: This token is only for the system admin or obtained via rails console. This token is to be used rarely for cases like creating a pre verified user through api from external system. + description: This token can be obtained by the system admin after creating a platformApp. This token should be used to provision agent bots, accounts, users and their roles. security: - userApiKey: [] @@ -42,3 +42,23 @@ definitions: $ref: ./definitions/index.yml parameters: $ref: ./parameters/index.yml + +x-tagGroups: + - name: Platform + tags: + - Accounts + - Account Users + - AgentBots + - Users + - name: Application + tags: + - Account AgentBots + - Contact + - Conversation + - Conversation Assignment + - Conversation Labels + - Inbox + - Messages + - Integrations + - Profile + - Teams \ No newline at end of file diff --git a/swagger/parameters/agent_bot_id.yml b/swagger/parameters/agent_bot_id.yml new file mode 100644 index 000000000..4d1479cb4 --- /dev/null +++ b/swagger/parameters/agent_bot_id.yml @@ -0,0 +1,6 @@ +in: path +name: id +schema: + type: integer +required: true +description: The ID of the agentbot to be updated diff --git a/swagger/parameters/index.yml b/swagger/parameters/index.yml index ac9c9a37b..daedad351 100644 --- a/swagger/parameters/index.yml +++ b/swagger/parameters/index.yml @@ -1,6 +1,9 @@ account_id: $ref: ./account_id.yml +agent_bot_id: + $ref: ./agent_bot_id.yml + team_id: $ref: ./team_id.yml diff --git a/swagger/paths/agent_bots/create.yml b/swagger/paths/agent_bots/create.yml new file mode 100644 index 000000000..e0fde6184 --- /dev/null +++ b/swagger/paths/agent_bots/create.yml @@ -0,0 +1,19 @@ +tags: + - Account AgentBots +operationId: create-an-account-agent-bot +summary: Create an Agent Bot +description: Create an agent bot in the account +parameters: + - $ref: '#/parameters/account_id' + - name: data + in: body + required: true + schema: + $ref: '#/definitions/agent_bot_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized diff --git a/swagger/paths/agent_bots/delete.yml b/swagger/paths/agent_bots/delete.yml new file mode 100644 index 000000000..0e3daf6cb --- /dev/null +++ b/swagger/paths/agent_bots/delete.yml @@ -0,0 +1,12 @@ +tags: + - Account AgentBots +operationId: delete-an-account-agent-bot +summary: Delete an AgentBot +description: Delete an AgentBot from the account +responses: + 200: + description: Success + 401: + description: Unauthorized + 404: + description: The agent bot does not exist in the account diff --git a/swagger/paths/agent_bots/index.yml b/swagger/paths/agent_bots/index.yml index 2fef38ff5..26766f5c8 100644 --- a/swagger/paths/agent_bots/index.yml +++ b/swagger/paths/agent_bots/index.yml @@ -1,17 +1,16 @@ -get: - tags: - - AgentBot - operationId: listAgentBots - summary: List all agentbots - description: List all available agentbots for the current installation - responses: - 200: - description: Success - schema: - type: Array - description: 'List of agent bots' - $ref: '#/definitions/agent_bot' - 404: - description: Inbox not found, Agent bot not found - 403: - description: Access denied +tags: + - Account AgentBots +operationId: list-all-account-agent-bots +summary: List all AgentBots +description: List all agent bots available for the current account +responses: + 200: + description: Success + schema: + type: array + description: 'Array of agent bots' + items: + - $ref: '#/definitions/agent_bot' + + 401: + description: Unauthorized diff --git a/swagger/paths/agent_bots/show.yml b/swagger/paths/agent_bots/show.yml new file mode 100644 index 000000000..b94637690 --- /dev/null +++ b/swagger/paths/agent_bots/show.yml @@ -0,0 +1,14 @@ +tags: + - Account AgentBots +operationId: get-details-of-a-single-account-agent-bot +summary: Get an agent bot details +description: Get the details of an agent bot in the account +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized + 404: + description: The given agent bot ID does not exist in the account diff --git a/swagger/paths/agent_bots/update.yml b/swagger/paths/agent_bots/update.yml new file mode 100644 index 000000000..562d58841 --- /dev/null +++ b/swagger/paths/agent_bots/update.yml @@ -0,0 +1,18 @@ +tags: + - Account AgentBots +operationId: update-an-account-agent-bot +summary: Update an agent bot +description: Update an agent bot's attributes +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/agent_bot_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized diff --git a/swagger/paths/inboxes/get_agent_bot.yml b/swagger/paths/inboxes/get_agent_bot.yml new file mode 100644 index 000000000..0f6c610cc --- /dev/null +++ b/swagger/paths/inboxes/get_agent_bot.yml @@ -0,0 +1,21 @@ +get: + tags: + - Inbox + operationId: getInboxAgentBot + summary: Show Inbox Agent Bot + description: See if an agent bot is associated to the Inbox + parameters: + - name: id + in: path + type: number + description: ID of the inbox + required: true + responses: + 204: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 404: + description: Inbox not found, Agent bot not found + 403: + description: Access denied diff --git a/swagger/paths/inboxes/index.yml b/swagger/paths/inboxes/index.yml index dcb0e4b10..7b5b29445 100644 --- a/swagger/paths/inboxes/index.yml +++ b/swagger/paths/inboxes/index.yml @@ -10,7 +10,8 @@ get: schema: type: array description: 'Array of inboxes' - $ref: '#/definitions/inbox' + items: + $ref: '#/definitions/inbox' 404: description: Inbox not found 403: diff --git a/swagger/paths/index.yml b/swagger/paths/index.yml index c8fd3be03..3c6b16185 100644 --- a/swagger/paths/index.yml +++ b/swagger/paths/index.yml @@ -1,61 +1,138 @@ +# ------------ Platform API routes ------------# + +# Accounts + +platform/api/v1/accounts: + post: + $ref: ./platform/accounts/create.yml +platform/api/v1/accounts/{id}: + parameters: + - $ref: '#/parameters/account_id' + get: + $ref: './platform/accounts/show.yml' + patch: + $ref: ./platform/accounts/update.yml + delete: + $ref: ./platform/accounts/delete.yml +# Account Users -# Inboxes -/accounts/{account_id}/inboxes: - $ref: ./inboxes/index.yml -/accounts/{account_id}/inboxes/: - $ref: ./inboxes/create.yml -/accounts/{account_id}/inboxes/{id}: - $ref: ./inboxes/update.yml -/accounts/{account_id}/inboxes/{id}/set_agent_bot: - $ref: ./inboxes/set_agent_bot.yml +platform/api/v1/accounts/{id}/account_users: + parameters: + - $ref: '#/parameters/account_id' + get: + $ref: './platform/account_users/index.yml' + post: + $ref: ./platform/account_users/create.yml + delete: + $ref: ./platform/account_users/delete.yml + +# AgentBots + +platform/api/v1/agent_bots: + get: + $ref: ./platform/agent_bots/index.yml + post: + $ref: ./platform/agent_bots/create.yml +platform/api/v1/agent_bots/{id}: + parameters: + - $ref: '#/parameters/agent_bot_id' + get: + $ref: './platform/agent_bots/show.yml' + patch: + $ref: ./platform/agent_bots/update.yml + delete: + $ref: ./platform/agent_bots/delete.yml + +# Users + +platform/api/v1/users: + post: + $ref: ./platform/users/create.yml +platform/api/v1/users/{id}: + parameters: + - $ref: '#/parameters/user_id' + get: + $ref: './platform/users/show.yml' + patch: + $ref: ./platform/users/update.yml + delete: + $ref: ./platform/users/delete.yml +platform/api/v1/users/{id}/login: + parameters: + - $ref: '#/parameters/user_id' + $ref: './platform/users/login.yml' + + +# ---------------- end of platform path -----------# + +# AgentBots + +/api/v1/accounts/{account_id}/agent_bots: + parameters: + - $ref: '#/parameters/account_id' + get: + $ref: ./agent_bots/index.yml + post: + $ref: ./agent_bots/create.yml +/api/v1/accounts/{account_id}/agent_bots/{id}: + parameters: + - $ref: '#/parameters/account_id' + - $ref: '#/parameters/agent_bot_id' + get: + $ref: './agent_bots/show.yml' + patch: + $ref: ./agent_bots/update.yml + delete: + $ref: ./agent_bots/delete.yml + + +# Contacts +/api/v1/accounts/{account_id}/contacts: + $ref: ./contact/list_create.yml +/api/v1/accounts/{account_id}/contacts/{id}: + $ref: ./contact/crud.yml +/api/v1/accounts/{account_id}/contacts/{id}/conversations: + $ref: ./contact/conversations.yml +/api/v1/accounts/{account_id}/contacts/search: + $ref: ./contact/search.yml +/api/v1/accounts/{account_id}/contacts/{id}/contact_inboxes: + $ref: ./contact_inboxes/create.yml +/api/v1/accounts/{account_id}/contacts/{id}/contactable_inboxes: + $ref: ./contactable_inboxes/get.yml -/agent_bots: - $ref: ./agent_bots/index.yml # Conversations -/accounts/{account_id}/conversations: +/api/v1/accounts/{account_id}/conversations: parameters: - $ref: '#/parameters/account_id' $ref: ./conversation/index_or_create.yml - -/accounts/{account_id}/conversations/{converstion_id}: +/api/v1/accounts/{account_id}/conversations/{converstion_id}: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/conversation_id' get: $ref: ./conversation/show.yml - -/accounts/{account_id}/conversations/{conversation_id}/toggle_status: +/api/v1/accounts/{account_id}/conversations/{conversation_id}/toggle_status: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/conversation_id' post: $ref: ./conversation/toggle_status.yml -# Messages -/accounts/{account_id}/conversations/{id}/messages: - $ref: ./conversation/messages/create_attachment.yml -/accounts/{account_id}/conversations/{converstion_id}/messages: +# Conversations Assignments + +/api/v1/accounts/{account_id}/conversations/{conversation_id}/assignments: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/conversation_id' - get: - $ref: ./conversation/messages/index.yml post: - $ref: ./conversation/messages/create.yml + $ref: ./conversation/assignments.yml -/accounts/{account_id}/conversations/{conversation_id}/messages/{message_id}: - parameters: - - $ref: '#/parameters/account_id' - - $ref: '#/parameters/conversation_id' - - $ref: '#/parameters/message_id' - delete: - $ref: ./conversation/messages/delete.yml +# Conversation Labels - -/accounts/{account_id}/conversations/{conversation_id}/labels: +/api/v1/accounts/{account_id}/conversations/{conversation_id}/labels: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/conversation_id' @@ -64,46 +141,71 @@ post: $ref: ./conversation/labels/create.yml -/accounts/{account_id}/conversations/{conversation_id}/assignments: + +# Inboxes +/api/v1/accounts/{account_id}/inboxes: + $ref: ./inboxes/index.yml +/api/v1/accounts/{account_id}/inboxes/: + $ref: ./inboxes/create.yml +/api/v1/accounts/{account_id}/inboxes/{id}: + $ref: ./inboxes/update.yml +/api/v1/accounts/{account_id}/inboxes/{id}/agent_bot: + $ref: ./inboxes/get_agent_bot.yml +/api/v1/accounts/{account_id}/inboxes/{id}/set_agent_bot: + $ref: ./inboxes/set_agent_bot.yml + + +# Messages +/api/v1/accounts/{account_id}/conversations/{id}/messages: + $ref: ./conversation/messages/create_attachment.yml +/api/v1/accounts/{account_id}/conversations/{converstion_id}/messages: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/conversation_id' + get: + $ref: ./conversation/messages/index.yml post: - $ref: ./conversation/assignments.yml + $ref: ./conversation/messages/create.yml +/api/v1/accounts/{account_id}/conversations/{conversation_id}/messages/{message_id}: + parameters: + - $ref: '#/parameters/account_id' + - $ref: '#/parameters/conversation_id' + - $ref: '#/parameters/message_id' + delete: + $ref: ./conversation/messages/delete.yml -# Contacts -/accounts/{account_id}/contacts: - $ref: ./contact/list_create.yml -/accounts/{account_id}/contacts/{id}: - $ref: ./contact/crud.yml -/accounts/{account_id}/contacts/{id}/conversations: - $ref: ./contact/conversations.yml +# Integrations +/api/v1/accounts/{account_id}/integrations/apps: + parameters: + - $ref: '#/parameters/account_id' + get: + $ref: './integrations/apps/show.yml' +/api/v1/accounts/{account_id}/integrations/hooks: + post: + $ref: './integrations/hooks/create.yml' + patch: + $ref: ./integrations/hooks/update.yml + delete: + $ref: ./integrations/hooks/delete.yml -/accounts/{account_id}/contacts/search: - $ref: ./contact/search.yml - -/accounts/{account_id}/contacts/{id}/contact_inboxes: - $ref: ./contact_inboxes/create.yml -/accounts/{account_id}/contacts/{id}/contactable_inboxes: - $ref: ./contactable_inboxes/get.yml # Profile /profile: $ref: ./profile/index.yml + # Teams -/accounts/{account_id}/teams: +/api/v1/accounts/{account_id}/teams: parameters: - $ref: '#/parameters/account_id' get: $ref: ./teams/index.yml post: $ref: ./teams/create.yml - -/accounts/{account_id}/teams/{id}: +/api/v1/accounts/{account_id}/teams/{id}: parameters: - $ref: '#/parameters/account_id' - $ref: '#/parameters/team_id' @@ -114,19 +216,3 @@ delete: $ref: ./teams/delete.yml - -# Integrations -/accounts/{account_id}/integrations/apps: - parameters: - - $ref: '#/parameters/account_id' - get: - $ref: './integrations/apps/show.yml' - - -/accounts/{account_id}/integrations/hooks: - post: - $ref: './integrations/hooks/create.yml' - patch: - $ref: ./integrations/hooks/update.yml - delete: - $ref: ./integrations/hooks/delete.yml \ No newline at end of file diff --git a/swagger/paths/integrations/apps/show.yml b/swagger/paths/integrations/apps/show.yml index 6d5f8332f..16ce299fe 100644 --- a/swagger/paths/integrations/apps/show.yml +++ b/swagger/paths/integrations/apps/show.yml @@ -7,7 +7,10 @@ responses: 200: description: Success schema: - $ref: '#/definitions/integrations_app' + type: array + description: 'Array of Integration apps' + items: + $ref: '#/definitions/integrations_app' 401: description: Unauthorized 404: diff --git a/swagger/paths/platform/account_users/create.yml b/swagger/paths/platform/account_users/create.yml new file mode 100644 index 000000000..614efb7a8 --- /dev/null +++ b/swagger/paths/platform/account_users/create.yml @@ -0,0 +1,40 @@ +tags: + - Account Users +operationId: create-an-account-user +summary: Create an Account User +description: Create an Account User +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + type: object + properties: + user_id: + type: integer + description: The ID of the user + required: true + role: + type: string + description: whether user is an administrator or agent + required: true + +responses: + 200: + description: Success + schema: + properties: + account_id: + type: integer + description: The ID of the user + user_id: + type: integer + description: The ID of the user + role: + type: string + description: whether user is an administrator or agent + + 401: + description: Unauthorized diff --git a/swagger/paths/platform/account_users/delete.yml b/swagger/paths/platform/account_users/delete.yml new file mode 100644 index 000000000..1ff822a9e --- /dev/null +++ b/swagger/paths/platform/account_users/delete.yml @@ -0,0 +1,26 @@ +tags: + - Account Users +operationId: delete-an-account-user +summary: Delete an Account User +description: Delete an Account User +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + type: object + properties: + user_id: + type: integer + description: The ID of the user + required: true + +responses: + 200: + description: Success + 401: + description: Unauthorized + 404: + description: The account does not exist diff --git a/swagger/paths/platform/account_users/index.yml b/swagger/paths/platform/account_users/index.yml new file mode 100644 index 000000000..bcaed1807 --- /dev/null +++ b/swagger/paths/platform/account_users/index.yml @@ -0,0 +1,28 @@ +tags: + - Account Users +operationId: list-all-account-users +summary: List all Account Users +description: List all account users +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + schema: + type: array + description: 'Array of account users' + items: + type: object + properties: + account_id: + type: integer + description: The ID of the user + user_id: + type: integer + description: The ID of the user + role: + type: string + description: whether user is an administrator or agent + + 401: + description: Unauthorized diff --git a/swagger/paths/platform/accounts/create.yml b/swagger/paths/platform/accounts/create.yml new file mode 100644 index 000000000..a6e9841b9 --- /dev/null +++ b/swagger/paths/platform/accounts/create.yml @@ -0,0 +1,20 @@ +tags: + - Accounts +operationId: create-an-account +summary: Create an Account +description: Create an Account +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/account_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/platform_account' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/accounts/delete.yml b/swagger/paths/platform/accounts/delete.yml new file mode 100644 index 000000000..755b76d1e --- /dev/null +++ b/swagger/paths/platform/accounts/delete.yml @@ -0,0 +1,14 @@ +tags: + - Accounts +operationId: delete-an-account +summary: Delete an Account +description: Delete an Account +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + 401: + description: Unauthorized + 404: + description: The account does not exist diff --git a/swagger/paths/platform/accounts/show.yml b/swagger/paths/platform/accounts/show.yml new file mode 100644 index 000000000..d8ec94fe6 --- /dev/null +++ b/swagger/paths/platform/accounts/show.yml @@ -0,0 +1,16 @@ +tags: + - Accounts +operationId: get-details-of-an-account +summary: Get an account details +description: Get the details of an account +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + schema: + $ref: '#/definitions/platform_account' + 401: + description: Unauthorized + 404: + description: The given account does not exist diff --git a/swagger/paths/platform/accounts/update.yml b/swagger/paths/platform/accounts/update.yml new file mode 100644 index 000000000..a70ce9468 --- /dev/null +++ b/swagger/paths/platform/accounts/update.yml @@ -0,0 +1,20 @@ +tags: + - Accounts +operationId: update-an-account +summary: Update an account +description: Update an account's attributes +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/account_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/platform_account' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/agent_bots/create.yml b/swagger/paths/platform/agent_bots/create.yml new file mode 100644 index 000000000..a862ca131 --- /dev/null +++ b/swagger/paths/platform/agent_bots/create.yml @@ -0,0 +1,20 @@ +tags: + - AgentBots +operationId: create-an-agent-bot +summary: Create an Agent Bot +description: Create an agent bot +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/agent_bot_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/agent_bots/delete.yml b/swagger/paths/platform/agent_bots/delete.yml new file mode 100644 index 000000000..676c8f525 --- /dev/null +++ b/swagger/paths/platform/agent_bots/delete.yml @@ -0,0 +1,14 @@ +tags: + - AgentBots +operationId: delete-an-agent-bot +summary: Delete an AgentBot +description: Delete an AgentBot +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + 401: + description: Unauthorized + 404: + description: The agent bot does not exist diff --git a/swagger/paths/platform/agent_bots/index.yml b/swagger/paths/platform/agent_bots/index.yml new file mode 100644 index 000000000..7ef3ee9c9 --- /dev/null +++ b/swagger/paths/platform/agent_bots/index.yml @@ -0,0 +1,17 @@ +tags: + - AgentBots +operationId: list-all-agent-bots +summary: List all AgentBots +description: List all agent bots available +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + schema: + type: array + description: 'Array of agent bots' + items: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/agent_bots/show.yml b/swagger/paths/platform/agent_bots/show.yml new file mode 100644 index 000000000..dfbd43a27 --- /dev/null +++ b/swagger/paths/platform/agent_bots/show.yml @@ -0,0 +1,16 @@ +tags: + - AgentBots +operationId: get-details-of-a-single-agent-bot +summary: Get an agent bot details +description: Get the details of an agent bot +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized + 404: + description: The given agent bot ID does not exist diff --git a/swagger/paths/platform/agent_bots/update.yml b/swagger/paths/platform/agent_bots/update.yml new file mode 100644 index 000000000..842b4854d --- /dev/null +++ b/swagger/paths/platform/agent_bots/update.yml @@ -0,0 +1,20 @@ +tags: + - AgentBots +operationId: update-an-agent-bot +summary: Update an agent bot +description: Update an agent bot's attributes +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/agent_bot_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/agent_bot' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/users/create.yml b/swagger/paths/platform/users/create.yml new file mode 100644 index 000000000..7433b8dc6 --- /dev/null +++ b/swagger/paths/platform/users/create.yml @@ -0,0 +1,20 @@ +tags: + - Users +operationId: create-a-user +summary: Create a User +description: Create a User +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/user_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/user' + 401: + description: Unauthorized diff --git a/swagger/paths/platform/users/delete.yml b/swagger/paths/platform/users/delete.yml new file mode 100644 index 000000000..a7658c215 --- /dev/null +++ b/swagger/paths/platform/users/delete.yml @@ -0,0 +1,14 @@ +tags: + - Users +operationId: delete-a-user +summary: Delete a User +description: Delete a User +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + 401: + description: Unauthorized + 404: + description: The user does not exist diff --git a/swagger/paths/platform/users/login.yml b/swagger/paths/platform/users/login.yml new file mode 100644 index 000000000..3f0b8b4c1 --- /dev/null +++ b/swagger/paths/platform/users/login.yml @@ -0,0 +1,21 @@ +get: + tags: + - Users + operationId: get-sso-url-of-a-user + summary: Get User SSO Link + description: Get the sso link of a user + security: + - platformAppApiKey: [] + responses: + 200: + description: Success + schema: + type: object + properties: + url: + type: string + description: SSO url to autenticate the user + 401: + description: Unauthorized + 404: + description: The given user does not exist diff --git a/swagger/paths/platform/users/show.yml b/swagger/paths/platform/users/show.yml new file mode 100644 index 000000000..bf937b49a --- /dev/null +++ b/swagger/paths/platform/users/show.yml @@ -0,0 +1,16 @@ +tags: + - Users +operationId: get-details-of-a-user +summary: Get an user details +description: Get the details of an user +security: + - platformAppApiKey: [] +responses: + 200: + description: Success + schema: + $ref: '#/definitions/user' + 401: + description: Unauthorized + 404: + description: The given user does not exist diff --git a/swagger/paths/platform/users/update.yml b/swagger/paths/platform/users/update.yml new file mode 100644 index 000000000..56ea0026d --- /dev/null +++ b/swagger/paths/platform/users/update.yml @@ -0,0 +1,20 @@ +tags: + - Users +operationId: update-a-user +summary: Update a user +description: Update a user's attributes +security: + - platformAppApiKey: [] +parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/user_create_update_payload' +responses: + 200: + description: Success + schema: + $ref: '#/definitions/user' + 401: + description: Unauthorized diff --git a/swagger/paths/teams/index.yml b/swagger/paths/teams/index.yml index 8e1384cf9..f93b6a553 100644 --- a/swagger/paths/teams/index.yml +++ b/swagger/paths/teams/index.yml @@ -9,6 +9,7 @@ responses: schema: type: array description: 'Array of teams' - $ref: '#/definitions/team' + items: + $ref: '#/definitions/team' 401: description: Unauthorized diff --git a/swagger/swagger.json b/swagger/swagger.json index ff823ed84..a4e7a9377 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -6,7 +6,7 @@ "title": "Chatwoot", "termsOfService": "https://www.chatwoot.com/terms-of-service/", "contact": { - "email": "support@chatwoot.com" + "email": "hello@chatwoot.com" }, "license": { "name": "MIT License", @@ -14,7 +14,7 @@ } }, "host": "app.chatwoot.com", - "basePath": "/api/v1/", + "basePath": null, "schemes": [ "https" ], @@ -37,11 +37,11 @@ "name": "api_access_token", "description": "This token should be provided by system admin or obtained via rails console. This token can be used to build bot integrations and can only access limited apis." }, - "superAdminApiKey": { + "platformAppApiKey": { "type": "apiKey", "in": "header", "name": "api_access_token", - "description": "This token is only for the system admin or obtained via rails console. This token is to be used rarely for cases like creating a pre verified user through api from external system." + "description": "This token can be obtained by the system admin after creating a platformApp. This token should be used to provision agent bots, accounts, users and their roles." } }, "security": [ @@ -52,86 +52,28 @@ } ], "paths": { - "/accounts/{account_id}/inboxes": { - "get": { - "tags": [ - "Inbox" - ], - "operationId": "listAllInboxes", - "summary": "List all inboxes", - "description": "List all inboxes available in the current account", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/inbox" - } - }, - "404": { - "description": "Inbox not found" - }, - "403": { - "description": "Access denied" - } - } - } - }, - "/accounts/{account_id}/inboxes/": { + "platform/api/v1/accounts": { "post": { "tags": [ - "Inbox" + "Accounts" + ], + "operationId": "create-an-account", + "summary": "Create an Account", + "description": "Create an Account", + "security": [ + { + "platformAppApiKey": [ + + ] + } ], - "operationId": "inboxCreation", - "summary": "Create an inbox", - "description": "You can create more than one website inbox in each account", "parameters": [ { "name": "data", "in": "body", "required": true, "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the inbox" - }, - "avatar": { - "type": "file", - "description": "File for avatar image" - }, - "channel": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "web_widget" - ] - }, - "website_url": { - "type": "string", - "description": "URL at which the widget will be loaded" - }, - "welcome_title": { - "type": "string", - "description": "Welcome title to be displayed on the widget" - }, - "welcome_tagline": { - "type": "string", - "description": "Welcome tagline to be displayed on the widget" - }, - "agent_away_message": { - "type": "string", - "description": "A message which will be sent if there is not agent available. This is not available if agentbot is connected" - }, - "widget_color": { - "type": "string", - "description": "A Hex-color string used to customize the widget" - } - } - } - } + "$ref": "#/definitions/account_create_update_payload" } } ], @@ -139,34 +81,178 @@ "200": { "description": "Success", "schema": { - "$ref": "#/definitions/inbox" + "$ref": "#/definitions/platform_account" } }, - "404": { - "description": "Inbox not found" - }, - "403": { - "description": "Access denied" + "401": { + "description": "Unauthorized" } } } }, - "/accounts/{account_id}/inboxes/{id}": { + "platform/api/v1/accounts/{id}": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + } + ], + "get": { + "tags": [ + "Accounts" + ], + "operationId": "get-details-of-an-account", + "summary": "Get an account details", + "description": "Get the details of an account", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/platform_account" + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The given account does not exist" + } + } + }, "patch": { "tags": [ - "Inbox" + "Accounts" + ], + "operationId": "update-an-account", + "summary": "Update an account", + "description": "Update an account's attributes", + "security": [ + { + "platformAppApiKey": [ + + ] + } ], - "operationId": "updateInbox", - "summary": "Update Inbox", - "description": "Add avatar and disable auto assignment for an inbox", "parameters": [ { - "name": "id", - "in": "path", - "type": "number", - "description": "ID of the inbox", - "required": true + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/account_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/platform_account" + } }, + "401": { + "description": "Unauthorized" + } + } + }, + "delete": { + "tags": [ + "Accounts" + ], + "operationId": "delete-an-account", + "summary": "Delete an Account", + "description": "Delete an Account", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The account does not exist" + } + } + } + }, + "platform/api/v1/accounts/{id}/account_users": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + } + ], + "get": { + "tags": [ + "Account Users" + ], + "operationId": "list-all-account-users", + "summary": "List all Account Users", + "description": "List all account users", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "description": "Array of account users", + "items": { + "type": "object", + "properties": { + "account_id": { + "type": "integer", + "description": "The ID of the user" + }, + "user_id": { + "type": "integer", + "description": "The ID of the user" + }, + "role": { + "type": "string", + "description": "whether user is an administrator or agent" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "post": { + "tags": [ + "Account Users" + ], + "operationId": "create-an-account-user", + "summary": "Create an Account User", + "description": "Create an Account User", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ { "name": "data", "in": "body", @@ -174,44 +260,15 @@ "schema": { "type": "object", "properties": { - "name": { + "user_id": { + "type": "integer", + "description": "The ID of the user", + "required": true + }, + "role": { "type": "string", - "description": "The name of the inbox" - }, - "enable_auto_assignment": { - "type": "boolean", - "required": true, - "description": "Enable Auto Assignment" - }, - "avatar": { - "type": "file", - "required": false, - "description": "Image file for avatar" - }, - "channel": { - "type": "object", - "properties": { - "website_url": { - "type": "string", - "description": "URL at which the widget will be loaded" - }, - "welcome_title": { - "type": "string", - "description": "Welcome title to be displayed on the widget" - }, - "welcome_tagline": { - "type": "string", - "description": "Welcome tagline to be displayed on the widget" - }, - "agent_away_message": { - "type": "string", - "description": "A message which will be sent if there is not agent available. This is not available if agentbot is connected" - }, - "widget_color": { - "type": "string", - "description": "A Hex-color string used to customize the widget" - } - } + "description": "whether user is an administrator or agent", + "required": true } } } @@ -221,34 +278,42 @@ "200": { "description": "Success", "schema": { - "$ref": "#/definitions/inbox" + "properties": { + "account_id": { + "type": "integer", + "description": "The ID of the user" + }, + "user_id": { + "type": "integer", + "description": "The ID of the user" + }, + "role": { + "type": "string", + "description": "whether user is an administrator or agent" + } + } } }, - "404": { - "description": "Inbox not found" - }, - "403": { - "description": "Access denied" + "401": { + "description": "Unauthorized" } } - } - }, - "/accounts/{account_id}/inboxes/{id}/set_agent_bot": { - "post": { + }, + "delete": { "tags": [ - "Inbox" + "Account Users" ], - "operationId": "updateAgentBot", - "summary": "Add or remove agent bot", - "description": "To add an agent bot pass agent_bot id, to remove agent bot from an inbox pass null", - "parameters": [ + "operationId": "delete-an-account-user", + "summary": "Delete an Account User", + "description": "Delete an Account User", + "security": [ { - "name": "id", - "in": "path", - "type": "number", - "description": "ID of the inbox", - "required": true - }, + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ { "name": "data", "in": "body", @@ -256,36 +321,83 @@ "schema": { "type": "object", "properties": { - "agent_bot": { - "type": "number", - "required": true, - "description": "Agent bot ID" + "user_id": { + "type": "integer", + "description": "The ID of the user", + "required": true } } } } ], "responses": { - "204": { + "200": { "description": "Success" }, - "404": { - "description": "Inbox not found, Agent bot not found" + "401": { + "description": "Unauthorized" }, - "403": { - "description": "Access denied" + "404": { + "description": "The account does not exist" } } } }, - "/agent_bots": { + "platform/api/v1/agent_bots": { "get": { "tags": [ - "AgentBot" + "AgentBots" + ], + "operationId": "list-all-agent-bots", + "summary": "List all AgentBots", + "description": "List all agent bots available", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "description": "Array of agent bots", + "items": { + "$ref": "#/definitions/agent_bot" + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "post": { + "tags": [ + "AgentBots" + ], + "operationId": "create-an-agent-bot", + "summary": "Create an Agent Bot", + "description": "Create an agent bot", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent_bot_create_update_payload" + } + } ], - "operationId": "listAgentBots", - "summary": "List all agentbots", - "description": "List all available agentbots for the current installation", "responses": { "200": { "description": "Success", @@ -293,8 +405,554 @@ "$ref": "#/definitions/agent_bot" } }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "platform/api/v1/agent_bots/{id}": { + "parameters": [ + { + "$ref": "#/parameters/agent_bot_id" + } + ], + "get": { + "tags": [ + "AgentBots" + ], + "operationId": "get-details-of-a-single-agent-bot", + "summary": "Get an agent bot details", + "description": "Get the details of an agent bot", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "401": { + "description": "Unauthorized" + }, "404": { - "description": "Inbox not found, Agent bot not found" + "description": "The given agent bot ID does not exist" + } + } + }, + "patch": { + "tags": [ + "AgentBots" + ], + "operationId": "update-an-agent-bot", + "summary": "Update an agent bot", + "description": "Update an agent bot's attributes", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent_bot_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "delete": { + "tags": [ + "AgentBots" + ], + "operationId": "delete-an-agent-bot", + "summary": "Delete an AgentBot", + "description": "Delete an AgentBot", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The agent bot does not exist" + } + } + } + }, + "platform/api/v1/users": { + "post": { + "tags": [ + "Users" + ], + "operationId": "create-a-user", + "summary": "Create a User", + "description": "Create a User", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/user_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/user" + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "platform/api/v1/users/{id}": { + "parameters": [ + { + "$ref": "#/parameters/user_id" + } + ], + "get": { + "tags": [ + "Users" + ], + "operationId": "get-details-of-a-user", + "summary": "Get an user details", + "description": "Get the details of an user", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/user" + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The given user does not exist" + } + } + }, + "patch": { + "tags": [ + "Users" + ], + "operationId": "update-a-user", + "summary": "Update a user", + "description": "Update a user's attributes", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/user_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/user" + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "delete": { + "tags": [ + "Users" + ], + "operationId": "delete-a-user", + "summary": "Delete a User", + "description": "Delete a User", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The user does not exist" + } + } + } + }, + "platform/api/v1/users/{id}/login": { + "get": { + "tags": [ + "Users" + ], + "operationId": "get-sso-url-of-a-user", + "summary": "Get User SSO Link", + "description": "Get the sso link of a user", + "security": [ + { + "platformAppApiKey": [ + + ] + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "SSO url to autenticate the user" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The given user does not exist" + } + } + } + }, + "/api/v1/accounts/{account_id}/agent_bots": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + } + ], + "get": { + "tags": [ + "Account AgentBots" + ], + "operationId": "list-all-account-agent-bots", + "summary": "List all AgentBots", + "description": "List all agent bots available for the current account", + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "description": "Array of agent bots", + "items": [ + { + "$ref": "#/definitions/agent_bot" + } + ] + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "post": { + "tags": [ + "Account AgentBots" + ], + "operationId": "create-an-account-agent-bot", + "summary": "Create an Agent Bot", + "description": "Create an agent bot in the account", + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent_bot_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/api/v1/accounts/{account_id}/agent_bots/{id}": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "$ref": "#/parameters/agent_bot_id" + } + ], + "get": { + "tags": [ + "Account AgentBots" + ], + "operationId": "get-details-of-a-single-account-agent-bot", + "summary": "Get an agent bot details", + "description": "Get the details of an agent bot in the account", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The given agent bot ID does not exist in the account" + } + } + }, + "patch": { + "tags": [ + "Account AgentBots" + ], + "operationId": "update-an-account-agent-bot", + "summary": "Update an agent bot", + "description": "Update an agent bot's attributes", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/agent_bot_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "delete": { + "tags": [ + "Account AgentBots" + ], + "operationId": "delete-an-account-agent-bot", + "summary": "Delete an AgentBot", + "description": "Delete an AgentBot from the account", + "responses": { + "200": { + "description": "Success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The agent bot does not exist in the account" + } + } + } + }, + "/api/v1/accounts/{account_id}/contacts": { + "get": { + "tags": [ + "Contact" + ], + "operationId": "contactList", + "description": "Listing all the contacts with pagination (Page size = 15)", + "summary": "List Contacts", + "parameters": [ + { + "$ref": "#/parameters/contact_sort_param" + }, + { + "$ref": "#/parameters/page" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/contact_list" + } + }, + "400": { + "description": "Bad Request Error", + "schema": { + "$ref": "#/definitions/bad_request_error" + } + } + } + }, + "post": { + "tags": [ + "Contact" + ], + "operationId": "contactCreate", + "description": "Create New Contact", + "summary": "Create Contact", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/contact_create" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/extended_contact" + } + }, + "400": { + "description": "Bad Request Error", + "schema": { + "$ref": "#/definitions/bad_request_error" + } + } + } + } + }, + "/api/v1/accounts/{account_id}/contacts/{id}": { + "get": { + "tags": [ + "Contact" + ], + "operationId": "contactDetails", + "summary": "Show Contact", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the contact", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/extended_contact" + } + }, + "404": { + "description": "Contact not found" + }, + "403": { + "description": "Access denied" + } + } + }, + "put": { + "tags": [ + "Contact" + ], + "operationId": "contactUpdate", + "summary": "Update Contact", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the contact", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/contact_update" + } + } + ], + "responses": { + "204": { + "description": "Success", + "schema": { + "$ref": "#/definitions/contact_base" + } + }, + "404": { + "description": "Contact not found" }, "403": { "description": "Access denied" @@ -302,7 +960,155 @@ } } }, - "/accounts/{account_id}/conversations": { + "/api/v1/accounts/{account_id}/contacts/{id}/conversations": { + "get": { + "tags": [ + "Contact" + ], + "operationId": "contactConversations", + "summary": "Conversations", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the contact", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/contact_conversations" + } + }, + "404": { + "description": "Contact not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/contacts/search": { + "get": { + "tags": [ + "Contact" + ], + "operationId": "contactSearch", + "description": "Search the contacts using a search key, currently supports email search (Page size = 15)", + "summary": "Search Contacts", + "parameters": [ + { + "name": "q", + "in": "query", + "type": "string" + }, + { + "$ref": "#/parameters/contact_sort_param" + }, + { + "$ref": "#/parameters/page" + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "object", + "properties": { + "payload": { + "$ref": "#/definitions/contact_list" + } + } + } + }, + "401": { + "description": "Authentication error", + "schema": { + "$ref": "#/definitions/bad_request_error" + } + } + } + } + }, + "/api/v1/accounts/{account_id}/contacts/{id}/contact_inboxes": { + "post": { + "tags": [ + "Contact" + ], + "operationId": "contactInboxCreation", + "description": "Create a contact inbox record for an inbox", + "summary": "Create contact inbox", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "inbox_id": { + "type": "number", + "description": "The ID of the inbox", + "required": true + }, + "source_id": { + "$ref": "#/parameters/source_id" + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/contact_inboxes" + } + }, + "401": { + "description": "Authentication error", + "schema": { + "$ref": "#/definitions/bad_request_error" + } + }, + "422": { + "description": "Incorrect payload" + } + } + } + }, + "/api/v1/accounts/{account_id}/contacts/{id}/contactable_inboxes": { + "get": { + "tags": [ + "Contact" + ], + "operationId": "contactableInboxesGet", + "description": "Get List of contactable Inboxes", + "summary": "Get Contactable Inboxes", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/contactable_inboxes" + } + }, + "401": { + "description": "Authentication error", + "schema": { + "$ref": "#/definitions/bad_request_error" + } + }, + "422": { + "description": "Incorrect payload" + } + } + } + }, + "/api/v1/accounts/{account_id}/conversations": { "get": { "tags": [ "Conversation" @@ -450,7 +1256,7 @@ } } }, - "/accounts/{account_id}/conversations/{converstion_id}": { + "/api/v1/accounts/{account_id}/conversations/{converstion_id}": { "parameters": [ { "$ref": "#/parameters/account_id" @@ -482,7 +1288,7 @@ } } }, - "/accounts/{account_id}/conversations/{conversation_id}/toggle_status": { + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/toggle_status": { "parameters": [ { "$ref": "#/parameters/account_id" @@ -548,7 +1354,395 @@ } } }, - "/accounts/{account_id}/conversations/{id}/messages": { + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/assignments": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "$ref": "#/parameters/conversation_id" + } + ], + "post": { + "tags": [ + "Conversation Assignment" + ], + "operationId": "assign-a-conversation", + "summary": "Assign Conversation", + "description": "Assign a conversation to an agent or a team", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "assignee_id": { + "type": "number", + "description": "Id of the assignee user" + }, + "team_id": { + "type": "number", + "description": "Id of the team. If the assignee_id is present, this param would be ignored" + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/user" + } + }, + "404": { + "description": "Conversation not found" + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/labels": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "$ref": "#/parameters/conversation_id" + } + ], + "get": { + "tags": [ + "Conversation Labels" + ], + "operationId": "list-all-labels-of-a-conversation", + "summary": "List Labels", + "description": "Lists all the labels of a conversation", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/conversation_labels" + } + }, + "404": { + "description": "Conversation not found" + }, + "401": { + "description": "Unauthorized" + } + } + }, + "post": { + "tags": [ + "Conversation Labels" + ], + "operationId": "conversation-add-labels", + "summary": "Add Labels", + "description": "Add labels to a conversation. Note that this API would overwrite the existing list of labels associated to the conversation.", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "labels": { + "type": "array", + "description": "Array of labels (comma-separated strings)", + "properties": { + "type": "string" + } + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/conversation_labels" + } + }, + "404": { + "description": "Conversation not found" + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/api/v1/accounts/{account_id}/inboxes": { + "get": { + "tags": [ + "Inbox" + ], + "operationId": "listAllInboxes", + "summary": "List all inboxes", + "description": "List all inboxes available in the current account", + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "description": "Array of inboxes", + "items": { + "$ref": "#/definitions/inbox" + } + } + }, + "404": { + "description": "Inbox not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/inboxes/": { + "post": { + "tags": [ + "Inbox" + ], + "operationId": "inboxCreation", + "summary": "Create an inbox", + "description": "You can create more than one website inbox in each account", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the inbox" + }, + "avatar": { + "type": "file", + "description": "File for avatar image" + }, + "channel": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "web_widget" + ] + }, + "website_url": { + "type": "string", + "description": "URL at which the widget will be loaded" + }, + "welcome_title": { + "type": "string", + "description": "Welcome title to be displayed on the widget" + }, + "welcome_tagline": { + "type": "string", + "description": "Welcome tagline to be displayed on the widget" + }, + "agent_away_message": { + "type": "string", + "description": "A message which will be sent if there is not agent available. This is not available if agentbot is connected" + }, + "widget_color": { + "type": "string", + "description": "A Hex-color string used to customize the widget" + } + } + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/inbox" + } + }, + "404": { + "description": "Inbox not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/inboxes/{id}": { + "patch": { + "tags": [ + "Inbox" + ], + "operationId": "updateInbox", + "summary": "Update Inbox", + "description": "Add avatar and disable auto assignment for an inbox", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the inbox", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the inbox" + }, + "enable_auto_assignment": { + "type": "boolean", + "required": true, + "description": "Enable Auto Assignment" + }, + "avatar": { + "type": "file", + "required": false, + "description": "Image file for avatar" + }, + "channel": { + "type": "object", + "properties": { + "website_url": { + "type": "string", + "description": "URL at which the widget will be loaded" + }, + "welcome_title": { + "type": "string", + "description": "Welcome title to be displayed on the widget" + }, + "welcome_tagline": { + "type": "string", + "description": "Welcome tagline to be displayed on the widget" + }, + "agent_away_message": { + "type": "string", + "description": "A message which will be sent if there is not agent available. This is not available if agentbot is connected" + }, + "widget_color": { + "type": "string", + "description": "A Hex-color string used to customize the widget" + } + } + } + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/inbox" + } + }, + "404": { + "description": "Inbox not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/inboxes/{id}/agent_bot": { + "get": { + "tags": [ + "Inbox" + ], + "operationId": "getInboxAgentBot", + "summary": "Show Inbox Agent Bot", + "description": "See if an agent bot is associated to the Inbox", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the inbox", + "required": true + } + ], + "responses": { + "204": { + "description": "Success", + "schema": { + "$ref": "#/definitions/agent_bot" + } + }, + "404": { + "description": "Inbox not found, Agent bot not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/inboxes/{id}/set_agent_bot": { + "post": { + "tags": [ + "Inbox" + ], + "operationId": "updateAgentBot", + "summary": "Add or remove agent bot", + "description": "To add an agent bot pass agent_bot id, to remove agent bot from an inbox pass null", + "parameters": [ + { + "name": "id", + "in": "path", + "type": "number", + "description": "ID of the inbox", + "required": true + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "agent_bot": { + "type": "number", + "required": true, + "description": "Agent bot ID" + } + } + } + } + ], + "responses": { + "204": { + "description": "Success" + }, + "404": { + "description": "Inbox not found, Agent bot not found" + }, + "403": { + "description": "Access denied" + } + } + } + }, + "/api/v1/accounts/{account_id}/conversations/{id}/messages": { "post": { "tags": [ "Messages" @@ -633,7 +1827,7 @@ } } }, - "/accounts/{account_id}/conversations/{converstion_id}/messages": { + "/api/v1/accounts/{account_id}/conversations/{converstion_id}/messages": { "parameters": [ { "$ref": "#/parameters/account_id" @@ -724,7 +1918,7 @@ } } }, - "/accounts/{account_id}/conversations/{conversation_id}/messages/{message_id}": { + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/messages/{message_id}": { "parameters": [ { "$ref": "#/parameters/account_id" @@ -756,573 +1950,7 @@ } } }, - "/accounts/{account_id}/conversations/{conversation_id}/labels": { - "parameters": [ - { - "$ref": "#/parameters/account_id" - }, - { - "$ref": "#/parameters/conversation_id" - } - ], - "get": { - "tags": [ - "Conversation Labels" - ], - "operationId": "list-all-labels-of-a-conversation", - "summary": "List Labels", - "description": "Lists all the labels of a conversation", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/conversation_labels" - } - }, - "404": { - "description": "Conversation not found" - }, - "401": { - "description": "Unauthorized" - } - } - }, - "post": { - "tags": [ - "Conversation Labels" - ], - "operationId": "conversation-add-labels", - "summary": "Add Labels", - "description": "Add labels to a conversation. Note that this API would overwrite the existing list of labels associated to the conversation.", - "parameters": [ - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "labels": { - "type": "array", - "description": "Array of labels (comma-separated strings)", - "properties": { - "type": "string" - } - } - } - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/conversation_labels" - } - }, - "404": { - "description": "Conversation not found" - }, - "401": { - "description": "Unauthorized" - } - } - } - }, - "/accounts/{account_id}/conversations/{conversation_id}/assignments": { - "parameters": [ - { - "$ref": "#/parameters/account_id" - }, - { - "$ref": "#/parameters/conversation_id" - } - ], - "post": { - "tags": [ - "Conversation Assignment" - ], - "operationId": "assign-a-conversation", - "summary": "Assign Conversation", - "description": "Assign a conversation to an agent or a team", - "parameters": [ - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "assignee_id": { - "type": "number", - "description": "Id of the assignee user" - }, - "team_id": { - "type": "number", - "description": "Id of the team. If the assignee_id is present, this param would be ignored" - } - } - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/user" - } - }, - "404": { - "description": "Conversation not found" - }, - "401": { - "description": "Unauthorized" - } - } - } - }, - "/accounts/{account_id}/contacts": { - "get": { - "tags": [ - "Contact" - ], - "operationId": "contactList", - "description": "Listing all the contacts with pagination (Page size = 15)", - "summary": "List Contacts", - "parameters": [ - { - "$ref": "#/parameters/contact_sort_param" - }, - { - "$ref": "#/parameters/page" - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/contact_list" - } - }, - "400": { - "description": "Bad Request Error", - "schema": { - "$ref": "#/definitions/bad_request_error" - } - } - } - }, - "post": { - "tags": [ - "Contact" - ], - "operationId": "contactCreate", - "description": "Create New Contact", - "summary": "Create Contact", - "parameters": [ - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/contact_create" - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/extended_contact" - } - }, - "400": { - "description": "Bad Request Error", - "schema": { - "$ref": "#/definitions/bad_request_error" - } - } - } - } - }, - "/accounts/{account_id}/contacts/{id}": { - "get": { - "tags": [ - "Contact" - ], - "operationId": "contactDetails", - "summary": "Show Contact", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "number", - "description": "ID of the contact", - "required": true - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/extended_contact" - } - }, - "404": { - "description": "Contact not found" - }, - "403": { - "description": "Access denied" - } - } - }, - "put": { - "tags": [ - "Contact" - ], - "operationId": "contactUpdate", - "summary": "Update Contact", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "number", - "description": "ID of the contact", - "required": true - }, - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/contact_update" - } - } - ], - "responses": { - "204": { - "description": "Success", - "schema": { - "$ref": "#/definitions/contact_base" - } - }, - "404": { - "description": "Contact not found" - }, - "403": { - "description": "Access denied" - } - } - } - }, - "/accounts/{account_id}/contacts/{id}/conversations": { - "get": { - "tags": [ - "Contact" - ], - "operationId": "contactConversations", - "summary": "Conversations", - "parameters": [ - { - "name": "id", - "in": "path", - "type": "number", - "description": "ID of the contact", - "required": true - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/contact_conversations" - } - }, - "404": { - "description": "Contact not found" - }, - "403": { - "description": "Access denied" - } - } - } - }, - "/accounts/{account_id}/contacts/search": { - "get": { - "tags": [ - "Contact" - ], - "operationId": "contactSearch", - "description": "Search the contacts using a search key, currently supports email search (Page size = 15)", - "summary": "Search Contacts", - "parameters": [ - { - "name": "q", - "in": "query", - "type": "string" - }, - { - "$ref": "#/parameters/contact_sort_param" - }, - { - "$ref": "#/parameters/page" - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "type": "object", - "properties": { - "payload": { - "$ref": "#/definitions/contact_list" - } - } - } - }, - "401": { - "description": "Authentication error", - "schema": { - "$ref": "#/definitions/bad_request_error" - } - } - } - } - }, - "/accounts/{account_id}/contacts/{id}/contact_inboxes": { - "post": { - "tags": [ - "Contact" - ], - "operationId": "contactInboxCreation", - "description": "Create a contact inbox record for an inbox", - "summary": "Create contact inbox", - "parameters": [ - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "type": "object", - "properties": { - "inbox_id": { - "type": "number", - "description": "The ID of the inbox", - "required": true - }, - "source_id": { - "$ref": "#/parameters/source_id" - } - } - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/contact_inboxes" - } - }, - "401": { - "description": "Authentication error", - "schema": { - "$ref": "#/definitions/bad_request_error" - } - }, - "422": { - "description": "Incorrect payload" - } - } - } - }, - "/accounts/{account_id}/contacts/{id}/contactable_inboxes": { - "get": { - "tags": [ - "Contact" - ], - "operationId": "contactableInboxesGet", - "description": "Get List of contactable Inboxes", - "summary": "Get Contactable Inboxes", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/contactable_inboxes" - } - }, - "401": { - "description": "Authentication error", - "schema": { - "$ref": "#/definitions/bad_request_error" - } - }, - "422": { - "description": "Incorrect payload" - } - } - } - }, - "/profile": { - "get": { - "tags": [ - "Profile" - ], - "operationId": "fetchProfile", - "summary": "Fetch user profile", - "description": "Get the user profile details", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/user" - } - }, - "401": { - "description": "Unauthorized" - } - } - } - }, - "/accounts/{account_id}/teams": { - "parameters": [ - { - "$ref": "#/parameters/account_id" - } - ], - "get": { - "tags": [ - "Teams" - ], - "operationId": "list-all-teams", - "summary": "List all teams", - "description": "List all teams available in the current account", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/team" - } - }, - "401": { - "description": "Unauthorized" - } - } - }, - "post": { - "tags": [ - "Teams" - ], - "operationId": "create-a-team", - "summary": "Create a team", - "description": "Create a team in the account", - "parameters": [ - { - "$ref": "#/parameters/account_id" - }, - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/team_create_update_payload" - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/team" - } - }, - "401": { - "description": "Unauthorized" - } - } - } - }, - "/accounts/{account_id}/teams/{id}": { - "parameters": [ - { - "$ref": "#/parameters/account_id" - }, - { - "$ref": "#/parameters/team_id" - } - ], - "get": { - "tags": [ - "Teams" - ], - "operationId": "get-details-of-a-single-team", - "summary": "Get a team details", - "description": "Get the details of a team in the account", - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/team" - } - }, - "401": { - "description": "Unauthorized" - }, - "404": { - "description": "The given team ID does not exist in the account" - } - } - }, - "patch": { - "tags": [ - "Teams" - ], - "operationId": "update-a-team", - "summary": "Update a team", - "description": "Update a team's attributes", - "parameters": [ - { - "name": "data", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/team_create_update_payload" - } - } - ], - "responses": { - "200": { - "description": "Success", - "schema": { - "$ref": "#/definitions/team" - } - }, - "401": { - "description": "Unauthorized" - } - } - }, - "delete": { - "tags": [ - "Teams" - ], - "operationId": "delete-a-team", - "summary": "Delete a team", - "description": "Delete a team from the account", - "responses": { - "200": { - "description": "Success" - }, - "401": { - "description": "Unauthorized" - }, - "404": { - "description": "The team does not exist in the account" - } - } - } - }, - "/accounts/{account_id}/integrations/apps": { + "/api/v1/accounts/{account_id}/integrations/apps": { "parameters": [ { "$ref": "#/parameters/account_id" @@ -1339,7 +1967,11 @@ "200": { "description": "Success", "schema": { - "$ref": "#/definitions/integrations_app" + "type": "array", + "description": "Array of Integration apps", + "items": { + "$ref": "#/definitions/integrations_app" + } } }, "401": { @@ -1351,7 +1983,7 @@ } } }, - "/accounts/{account_id}/integrations/hooks": { + "/api/v1/accounts/{account_id}/integrations/hooks": { "post": { "tags": [ "Integrations" @@ -1446,6 +2078,169 @@ } } } + }, + "/profile": { + "get": { + "tags": [ + "Profile" + ], + "operationId": "fetchProfile", + "summary": "Fetch user profile", + "description": "Get the user profile details", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/user" + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/api/v1/accounts/{account_id}/teams": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + } + ], + "get": { + "tags": [ + "Teams" + ], + "operationId": "list-all-teams", + "summary": "List all teams", + "description": "List all teams available in the current account", + "responses": { + "200": { + "description": "Success", + "schema": { + "type": "array", + "description": "Array of teams", + "items": { + "$ref": "#/definitions/team" + } + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "post": { + "tags": [ + "Teams" + ], + "operationId": "create-a-team", + "summary": "Create a team", + "description": "Create a team in the account", + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/team_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/team" + } + }, + "401": { + "description": "Unauthorized" + } + } + } + }, + "/api/v1/accounts/{account_id}/teams/{id}": { + "parameters": [ + { + "$ref": "#/parameters/account_id" + }, + { + "$ref": "#/parameters/team_id" + } + ], + "get": { + "tags": [ + "Teams" + ], + "operationId": "get-details-of-a-single-team", + "summary": "Get a team details", + "description": "Get the details of a team in the account", + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/team" + } + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The given team ID does not exist in the account" + } + } + }, + "patch": { + "tags": [ + "Teams" + ], + "operationId": "update-a-team", + "summary": "Update a team", + "description": "Update a team's attributes", + "parameters": [ + { + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/team_create_update_payload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/team" + } + }, + "401": { + "description": "Unauthorized" + } + } + }, + "delete": { + "tags": [ + "Teams" + ], + "operationId": "delete-a-team", + "summary": "Delete a team", + "description": "Delete a team from the account", + "responses": { + "200": { + "description": "Success" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "The team does not exist in the account" + } + } + } } }, "definitions": { @@ -1723,13 +2518,17 @@ "type": "number", "description": "ID of the agent bot" }, + "name": { + "type": "string", + "description": "The name of the agent bot" + }, "description": { "type": "string", "description": "The description about the agent bot" }, - "name": { - "type": "string", - "description": "The name of the agent bot" + "account_id": { + "type": "number", + "description": "Account ID if it's an account specific bot" }, "outgoing_url": { "type": "string", @@ -1782,6 +2581,19 @@ } } }, + "platform_account": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "Account ID" + }, + "name": { + "type": "string", + "description": "Name of the account" + } + } + }, "team": { "type": "object", "properties": { @@ -2154,6 +2966,41 @@ } ] }, + "account_create_update_payload": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the account" + } + } + }, + "agent_bot_create_update_payload": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the agent bot" + }, + "description": { + "type": "string", + "description": "The description about the agent bot" + }, + "outgoing_url": { + "type": "string", + "description": "The webhook URL for the bot" + } + } + }, + "user_create_update_payload": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the account" + } + } + }, "contact_create": { "type": "object", "properties": { @@ -2277,6 +3124,15 @@ "required": true, "description": "The numeric ID of the account" }, + "agent_bot_id": { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true, + "description": "The ID of the agentbot to be updated" + }, "team_id": { "in": "path", "name": "id", @@ -2346,5 +3202,31 @@ "required": false, "description": "The page parameter" } - } + }, + "x-tagGroups": [ + { + "name": "Platform", + "tags": [ + "Accounts", + "Account Users", + "AgentBots", + "Users" + ] + }, + { + "name": "Application", + "tags": [ + "Account AgentBots", + "Contact", + "Conversation", + "Conversation Assignment", + "Conversation Labels", + "Inbox", + "Messages", + "Integrations", + "Profile", + "Teams" + ] + } + ] } \ No newline at end of file From 22965be6dc9cbbf9143f3c37552182c163988638 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 1 Jun 2021 22:34:25 +0530 Subject: [PATCH 22/67] feat: Add AgentBot APIs (#2323) --- .../api/v1/accounts/agent_bots_controller.rb | 35 ++++ .../api/v1/accounts/inboxes_controller.rb | 4 + .../api/v1/agent_bots_controller.rb | 7 - .../platform/api/v1/agent_bots_controller.rb | 35 ++++ app/dashboards/agent_bot_dashboard.rb | 5 +- app/javascript/widget/mixins/configMixin.js | 3 - .../widget/mixins/specs/configMixin.spec.js | 3 - app/javascript/widget/views/Home.vue | 14 +- app/models/account.rb | 1 + app/models/agent_bot.rb | 23 ++- app/policies/agent_bot_policy.rb | 21 ++ app/policies/inbox_policy.rb | 4 + .../accounts/agent_bots/create.json.jbuilder | 1 + .../accounts/agent_bots/index.json.jbuilder | 3 + .../v1/accounts/agent_bots/show.json.jbuilder | 1 + .../accounts/agent_bots/update.json.jbuilder | 1 + .../accounts/inboxes/agent_bot.json.jbuilder | 3 + .../api/v1/models/_agent_bot.json.jbuilder | 5 + .../api/v1/agent_bots/create.json.jbuilder | 1 + .../api/v1/agent_bots/index.json.jbuilder | 3 + .../api/v1/agent_bots/show.json.jbuilder | 1 + .../api/v1/agent_bots/update.json.jbuilder | 1 + .../api/v1/models/_agent_bot.json.jbuilder | 5 + app/views/widgets/show.html.erb | 3 - config/routes.rb | 6 +- ...0520200729_add_account_id_to_agent_bots.rb | 6 + db/schema.rb | 6 +- .../v1/accounts/agent_bots_controller_spec.rb | 180 ++++++++++++++++++ .../v1/accounts/inboxes_controller_spec.rb | 49 ++++- .../api/v1/agent_bots_controller_spec.rb | 17 -- .../api/v1/agent_bots_controller_spec.rb | 172 +++++++++++++++++ 31 files changed, 552 insertions(+), 67 deletions(-) create mode 100644 app/controllers/api/v1/accounts/agent_bots_controller.rb delete mode 100644 app/controllers/api/v1/agent_bots_controller.rb create mode 100644 app/controllers/platform/api/v1/agent_bots_controller.rb create mode 100644 app/policies/agent_bot_policy.rb create mode 100644 app/views/api/v1/accounts/agent_bots/create.json.jbuilder create mode 100644 app/views/api/v1/accounts/agent_bots/index.json.jbuilder create mode 100644 app/views/api/v1/accounts/agent_bots/show.json.jbuilder create mode 100644 app/views/api/v1/accounts/agent_bots/update.json.jbuilder create mode 100644 app/views/api/v1/accounts/inboxes/agent_bot.json.jbuilder create mode 100644 app/views/api/v1/models/_agent_bot.json.jbuilder create mode 100644 app/views/platform/api/v1/agent_bots/create.json.jbuilder create mode 100644 app/views/platform/api/v1/agent_bots/index.json.jbuilder create mode 100644 app/views/platform/api/v1/agent_bots/show.json.jbuilder create mode 100644 app/views/platform/api/v1/agent_bots/update.json.jbuilder create mode 100644 app/views/platform/api/v1/models/_agent_bot.json.jbuilder create mode 100644 db/migrate/20210520200729_add_account_id_to_agent_bots.rb create mode 100644 spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb delete mode 100644 spec/controllers/api/v1/agent_bots_controller_spec.rb create mode 100644 spec/controllers/platform/api/v1/agent_bots_controller_spec.rb diff --git a/app/controllers/api/v1/accounts/agent_bots_controller.rb b/app/controllers/api/v1/accounts/agent_bots_controller.rb new file mode 100644 index 000000000..7348ef255 --- /dev/null +++ b/app/controllers/api/v1/accounts/agent_bots_controller.rb @@ -0,0 +1,35 @@ +class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController + before_action :current_account + before_action :check_authorization + before_action :agent_bot, except: [:index, :create] + + def index + @agent_bots = AgentBot.where(account_id: [nil, Current.account.id]) + end + + def show; end + + def create + @agent_bot = Current.account.agent_bots.create!(permitted_params) + end + + def update + @agent_bot.update!(permitted_params) + end + + def destroy + @agent_bot.destroy + head :ok + end + + private + + def agent_bot + @agent_bot = AgentBot.where(account_id: [nil, Current.account.id]).find(params[:id]) if params[:action] == 'show' + @agent_bot ||= Current.account.agent_bots.find(params[:id]) + end + + def permitted_params + params.permit(:name, :description, :outgoing_url) + end +end diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb index 014e213d4..8a5f5b9b9 100644 --- a/app/controllers/api/v1/accounts/inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/inboxes_controller.rb @@ -38,6 +38,10 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController update_channel_feature_flags end + def agent_bot + @agent_bot = @inbox.agent_bot + end + def set_agent_bot if @agent_bot agent_bot_inbox = @inbox.agent_bot_inbox || AgentBotInbox.new(inbox: @inbox) diff --git a/app/controllers/api/v1/agent_bots_controller.rb b/app/controllers/api/v1/agent_bots_controller.rb deleted file mode 100644 index a82f71bb9..000000000 --- a/app/controllers/api/v1/agent_bots_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Api::V1::AgentBotsController < Api::BaseController - skip_before_action :authenticate_user! - - def index - render json: AgentBot.all - end -end diff --git a/app/controllers/platform/api/v1/agent_bots_controller.rb b/app/controllers/platform/api/v1/agent_bots_controller.rb new file mode 100644 index 000000000..0c79e118d --- /dev/null +++ b/app/controllers/platform/api/v1/agent_bots_controller.rb @@ -0,0 +1,35 @@ +class Platform::Api::V1::AgentBotsController < PlatformController + before_action :set_resource, except: [:index, :create] + before_action :validate_platform_app_permissible, except: [:index, :create] + + def index + @resources = @platform_app.platform_app_permissibles.where(permissible_type: 'AgentBot').all + end + + def create + @resource = AgentBot.new(agent_bot_params) + @resource.save! + @platform_app.platform_app_permissibles.find_or_create_by(permissible: @resource) + end + + def show; end + + def update + @resource.update!(agent_bot_params) + end + + def destroy + @resource.destroy! + head :ok + end + + private + + def set_resource + @resource = AgentBot.find(params[:id]) + end + + def agent_bot_params + params.permit(:name, :description, :account_id, :outgoing_url) + end +end diff --git a/app/dashboards/agent_bot_dashboard.rb b/app/dashboards/agent_bot_dashboard.rb index ca6ca7ad1..87605135d 100644 --- a/app/dashboards/agent_bot_dashboard.rb +++ b/app/dashboards/agent_bot_dashboard.rb @@ -15,8 +15,7 @@ class AgentBotDashboard < Administrate::BaseDashboard description: Field::String, outgoing_url: Field::String, created_at: Field::DateTime, - updated_at: Field::DateTime, - hide_input_for_bot_conversations: Field::Boolean + updated_at: Field::DateTime }.freeze # COLLECTION_ATTRIBUTES @@ -39,7 +38,6 @@ class AgentBotDashboard < Administrate::BaseDashboard name description outgoing_url - hide_input_for_bot_conversations ].freeze # FORM_ATTRIBUTES @@ -49,7 +47,6 @@ class AgentBotDashboard < Administrate::BaseDashboard name description outgoing_url - hide_input_for_bot_conversations ].freeze # COLLECTION_FILTERS diff --git a/app/javascript/widget/mixins/configMixin.js b/app/javascript/widget/mixins/configMixin.js index df26add3b..c70d9ca57 100644 --- a/app/javascript/widget/mixins/configMixin.js +++ b/app/javascript/widget/mixins/configMixin.js @@ -1,8 +1,5 @@ export default { computed: { - hideInputForBotConversations() { - return window.chatwootWebChannel.hideInputForBotConversations; - }, useInboxAvatarForBot() { return window.chatwootWidgetDefaults.useInboxAvatarForBot; }, diff --git a/app/javascript/widget/mixins/specs/configMixin.spec.js b/app/javascript/widget/mixins/specs/configMixin.spec.js index 014a7930a..663cf695d 100644 --- a/app/javascript/widget/mixins/specs/configMixin.spec.js +++ b/app/javascript/widget/mixins/specs/configMixin.spec.js @@ -3,7 +3,6 @@ import configMixin from '../configMixin'; import Vue from 'vue'; global.chatwootWebChannel = { - hideInputForBotConversations: true, avatarUrl: 'https://test.url', hasAConnectedAgentBot: 'AgentBot', enabledFeatures: ['emoji_picker', 'attachments'], @@ -25,12 +24,10 @@ describe('configMixin', () => { const wrapper = createWrapper(vm); expect(wrapper.vm.hasEmojiPickerEnabled).toBe(true); expect(wrapper.vm.hasAttachmentsEnabled).toBe(true); - expect(wrapper.vm.hideInputForBotConversations).toBe(true); expect(wrapper.vm.hasAConnectedAgentBot).toBe(true); expect(wrapper.vm.useInboxAvatarForBot).toBe(true); expect(wrapper.vm.inboxAvatarUrl).toBe('https://test.url'); expect(wrapper.vm.channelConfig).toEqual({ - hideInputForBotConversations: true, avatarUrl: 'https://test.url', hasAConnectedAgentBot: 'AgentBot', enabledFeatures: ['emoji_picker', 'attachments'], diff --git a/app/javascript/widget/views/Home.vue b/app/javascript/widget/views/Home.vue index 3d77f33b5..a86c29a28 100755 --- a/app/javascript/widget/views/Home.vue +++ b/app/javascript/widget/views/Home.vue @@ -62,10 +62,7 @@ leave-class="opacity-100 transform translate-y-0" leave-to-class="opacity-0 transform " > -
+
accounts.id) # class AgentBot < ApplicationRecord @@ -18,6 +26,7 @@ class AgentBot < ApplicationRecord has_many :agent_bot_inboxes, dependent: :destroy has_many :inboxes, through: :agent_bot_inboxes has_many :messages, as: :sender, dependent: :restrict_with_exception + belongs_to :account, dependent: :destroy, optional: true def available_name name diff --git a/app/policies/agent_bot_policy.rb b/app/policies/agent_bot_policy.rb new file mode 100644 index 000000000..4d5e363ea --- /dev/null +++ b/app/policies/agent_bot_policy.rb @@ -0,0 +1,21 @@ +class AgentBotPolicy < ApplicationPolicy + def index? + @account_user.administrator? || @account_user.agent? + end + + def update? + @account_user.administrator? + end + + def show? + @account_user.administrator? || @account_user.agent? + end + + def create? + @account_user.administrator? + end + + def destroy? + @account_user.administrator? + end +end diff --git a/app/policies/inbox_policy.rb b/app/policies/inbox_policy.rb index 927ff9435..0bfe6fe40 100644 --- a/app/policies/inbox_policy.rb +++ b/app/policies/inbox_policy.rb @@ -27,6 +27,10 @@ class InboxPolicy < ApplicationPolicy true end + def agent_bot? + true + end + def campaigns? @account_user.administrator? end diff --git a/app/views/api/v1/accounts/agent_bots/create.json.jbuilder b/app/views/api/v1/accounts/agent_bots/create.json.jbuilder new file mode 100644 index 000000000..453ef5cb4 --- /dev/null +++ b/app/views/api/v1/accounts/agent_bots/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/agent_bot.json.jbuilder', resource: @agent_bot diff --git a/app/views/api/v1/accounts/agent_bots/index.json.jbuilder b/app/views/api/v1/accounts/agent_bots/index.json.jbuilder new file mode 100644 index 000000000..d65188ff2 --- /dev/null +++ b/app/views/api/v1/accounts/agent_bots/index.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @agent_bots do |agent_bot| + json.partial! 'api/v1/models/agent_bot.json.jbuilder', resource: agent_bot +end diff --git a/app/views/api/v1/accounts/agent_bots/show.json.jbuilder b/app/views/api/v1/accounts/agent_bots/show.json.jbuilder new file mode 100644 index 000000000..453ef5cb4 --- /dev/null +++ b/app/views/api/v1/accounts/agent_bots/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/agent_bot.json.jbuilder', resource: @agent_bot diff --git a/app/views/api/v1/accounts/agent_bots/update.json.jbuilder b/app/views/api/v1/accounts/agent_bots/update.json.jbuilder new file mode 100644 index 000000000..453ef5cb4 --- /dev/null +++ b/app/views/api/v1/accounts/agent_bots/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/agent_bot.json.jbuilder', resource: @agent_bot diff --git a/app/views/api/v1/accounts/inboxes/agent_bot.json.jbuilder b/app/views/api/v1/accounts/inboxes/agent_bot.json.jbuilder new file mode 100644 index 000000000..73b364f04 --- /dev/null +++ b/app/views/api/v1/accounts/inboxes/agent_bot.json.jbuilder @@ -0,0 +1,3 @@ +json.agent_bot do + json.partial! 'api/v1/models/agent_bot.json.jbuilder', resource: @agent_bot if @agent_bot.present? +end diff --git a/app/views/api/v1/models/_agent_bot.json.jbuilder b/app/views/api/v1/models/_agent_bot.json.jbuilder new file mode 100644 index 000000000..1aa152186 --- /dev/null +++ b/app/views/api/v1/models/_agent_bot.json.jbuilder @@ -0,0 +1,5 @@ +json.id resource.id +json.name resource.name +json.description resource.description +json.outgoing_url resource.name +json.account_id resource.account_id diff --git a/app/views/platform/api/v1/agent_bots/create.json.jbuilder b/app/views/platform/api/v1/agent_bots/create.json.jbuilder new file mode 100644 index 000000000..32123ab29 --- /dev/null +++ b/app/views/platform/api/v1/agent_bots/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'platform/api/v1/models/agent_bot.json.jbuilder', resource: @resource diff --git a/app/views/platform/api/v1/agent_bots/index.json.jbuilder b/app/views/platform/api/v1/agent_bots/index.json.jbuilder new file mode 100644 index 000000000..9dd6dcce6 --- /dev/null +++ b/app/views/platform/api/v1/agent_bots/index.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @resources do |resource| + json.partial! 'platform/api/v1/models/agent_bot.json.jbuilder', resource: resource.permissible +end diff --git a/app/views/platform/api/v1/agent_bots/show.json.jbuilder b/app/views/platform/api/v1/agent_bots/show.json.jbuilder new file mode 100644 index 000000000..32123ab29 --- /dev/null +++ b/app/views/platform/api/v1/agent_bots/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'platform/api/v1/models/agent_bot.json.jbuilder', resource: @resource diff --git a/app/views/platform/api/v1/agent_bots/update.json.jbuilder b/app/views/platform/api/v1/agent_bots/update.json.jbuilder new file mode 100644 index 000000000..32123ab29 --- /dev/null +++ b/app/views/platform/api/v1/agent_bots/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'platform/api/v1/models/agent_bot.json.jbuilder', resource: @resource diff --git a/app/views/platform/api/v1/models/_agent_bot.json.jbuilder b/app/views/platform/api/v1/models/_agent_bot.json.jbuilder new file mode 100644 index 000000000..1aa152186 --- /dev/null +++ b/app/views/platform/api/v1/models/_agent_bot.json.jbuilder @@ -0,0 +1,5 @@ +json.id resource.id +json.name resource.name +json.description resource.description +json.outgoing_url resource.name +json.account_id resource.account_id diff --git a/app/views/widgets/show.html.erb b/app/views/widgets/show.html.erb index 85297111a..c63dd08eb 100644 --- a/app/views/widgets/show.html.erb +++ b/app/views/widgets/show.html.erb @@ -8,9 +8,6 @@ window.chatwootWebChannel = { avatarUrl: '<%= @web_widget.inbox.avatar_url %>', hasAConnectedAgentBot: '<%= @web_widget.inbox.agent_bot&.name %>', - <% if @web_widget.inbox.agent_bot %> - hideInputForBotConversations: <%= @web_widget.inbox.agent_bot.hide_input_for_bot_conversations %>, - <% end %> locale: '<%= @web_widget.account.locale %>', websiteName: '<%= @web_widget.inbox.name %>', websiteToken: '<%= @web_widget.website_token %>', diff --git a/config/routes.rb b/config/routes.rb index bbfe3599b..499588e42 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,6 +38,8 @@ Rails.application.routes.draw do end resources :agents, except: [:show, :edit, :new] + resources :agent_bots, only: [:index, :create, :show, :update, :destroy] + resources :callbacks, only: [] do collection do post :register_facebook_page @@ -97,6 +99,7 @@ Rails.application.routes.draw do resources :inboxes, only: [:index, :create, :update, :destroy] do get :assignable_agents, on: :member get :campaigns, on: :member + get :agent_bot, on: :member post :set_agent_bot, on: :member end resources :inbox_members, only: [:create, :show], param: :inbox_id @@ -151,8 +154,6 @@ Rails.application.routes.draw do resource :profile, only: [:show, :update] resource :notification_subscriptions, only: [:create] - resources :agent_bots, only: [:index] - namespace :widget do resources :campaigns, only: [:index] resources :events, only: [:create] @@ -194,6 +195,7 @@ Rails.application.routes.draw do get :login end end + resources :agent_bots, only: [:index, :create, :show, :update, :destroy] resources :accounts, only: [:create, :show, :update, :destroy] do resources :account_users, only: [:index, :create] do collection do diff --git a/db/migrate/20210520200729_add_account_id_to_agent_bots.rb b/db/migrate/20210520200729_add_account_id_to_agent_bots.rb new file mode 100644 index 000000000..cd4a26073 --- /dev/null +++ b/db/migrate/20210520200729_add_account_id_to_agent_bots.rb @@ -0,0 +1,6 @@ +class AddAccountIdToAgentBots < ActiveRecord::Migration[6.0] + def change + remove_column :agent_bots, :hide_input_for_bot_conversations, :boolean + add_reference :agent_bots, :account, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 60f080d5c..5a1fd235b 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.define(version: 2021_05_13_083044) do +ActiveRecord::Schema.define(version: 2021_05_20_200729) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -97,7 +97,8 @@ ActiveRecord::Schema.define(version: 2021_05_13_083044) do t.string "outgoing_url" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false - t.boolean "hide_input_for_bot_conversations", default: false + t.bigint "account_id" + t.index ["account_id"], name: "index_agent_bots_on_account_id" end create_table "attachments", id: :serial, force: :cascade do |t| @@ -611,6 +612,7 @@ ActiveRecord::Schema.define(version: 2021_05_13_083044) do add_foreign_key "account_users", "accounts" add_foreign_key "account_users", "users" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" + add_foreign_key "agent_bots", "accounts" add_foreign_key "campaigns", "accounts" add_foreign_key "campaigns", "inboxes" add_foreign_key "contact_inboxes", "contacts" diff --git a/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb b/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb new file mode 100644 index 000000000..60442e6d6 --- /dev/null +++ b/spec/controllers/api/v1/accounts/agent_bots_controller_spec.rb @@ -0,0 +1,180 @@ +require 'rails_helper' + +RSpec.describe 'Agent Bot API', type: :request do + let!(:account) { create(:account) } + let!(:agent_bot) { create(:agent_bot, account: account) } + let(:admin) { create(:user, account: account, role: :administrator) } + let(:agent) { create(:user, account: account, role: :agent) } + + describe 'GET /api/v1/accounts/{account.id}/agent_bots' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/agent_bots" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'returns all the agent_bots in account along with global agent bots' do + global_bot = create(:agent_bot) + get "/api/v1/accounts/#{account.id}/agent_bots", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(agent_bot.name) + expect(response.body).to include(global_bot.name) + end + end + end + + describe 'GET /api/v1/accounts/{account.id}/agent_bots/:id' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'shows the agent bot' do + get "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(agent_bot.name) + end + + it 'will show a global agent bot' do + global_bot = create(:agent_bot) + get "/api/v1/accounts/#{account.id}/agent_bots/#{global_bot.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(response.body).to include(global_bot.name) + end + end + end + + describe 'POST /api/v1/accounts/{account.id}/agent_bots' do + let(:valid_params) { { name: 'test' } } + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + expect { post "/api/v1/accounts/#{account.id}/agent_bots", params: valid_params }.to change(Label, :count).by(0) + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'creates the agent bot when administrator' do + expect do + post "/api/v1/accounts/#{account.id}/agent_bots", headers: admin.create_new_auth_token, + params: valid_params + end.to change(AgentBot, :count).by(1) + + expect(response).to have_http_status(:success) + end + + it 'would not create the agent bot when agent' do + expect do + post "/api/v1/accounts/#{account.id}/agent_bots", headers: agent.create_new_auth_token, + params: valid_params + end.to change(AgentBot, :count).by(0) + + expect(response).to have_http_status(:unauthorized) + end + end + end + + describe 'PATCH /api/v1/accounts/{account.id}/agent_bots/:id' do + let(:valid_params) { { name: 'test_updated' } } + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + patch "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + params: valid_params + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'updates the agent bot' do + patch "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: admin.create_new_auth_token, + params: valid_params, + as: :json + + expect(response).to have_http_status(:success) + expect(agent_bot.reload.name).to eq('test_updated') + end + + it 'would not update the agent bot when agent' do + patch "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: agent.create_new_auth_token, + params: valid_params, + as: :json + + expect(response).to have_http_status(:unauthorized) + expect(agent_bot.reload.name).not_to eq('test_updated') + end + + it 'would not update a global agent bot' do + global_bot = create(:agent_bot) + patch "/api/v1/accounts/#{account.id}/agent_bots/#{global_bot.id}", + headers: admin.create_new_auth_token, + params: valid_params, + as: :json + + expect(response).to have_http_status(:not_found) + expect(agent_bot.reload.name).not_to eq('test_updated') + end + end + end + + describe 'DELETE /api/v1/accounts/{account.id}/agent_bots/:id' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + delete "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'deletes an agent bot when administrator' do + delete "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(account.agent_bots.size).to eq(0) + end + + it 'would not delete the agent bot when agent' do + delete "/api/v1/accounts/#{account.id}/agent_bots/#{agent_bot.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:unauthorized) + expect(account.agent_bots.size).not_to eq(0) + end + + it 'would not delete a global agent bot' do + global_bot = create(:agent_bot) + delete "/api/v1/accounts/#{account.id}/agent_bots/#{global_bot.id}", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:not_found) + expect(account.agent_bots.size).not_to eq(0) + end + end + end +end diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb index 5efa8049c..a4143df25 100644 --- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb @@ -2,6 +2,8 @@ require 'rails_helper' RSpec.describe 'Inboxes API', type: :request do let(:account) { create(:account) } + let(:agent) { create(:user, account: account, role: :agent) } + let(:admin) { create(:user, account: account, role: :administrator) } describe 'GET /api/v1/accounts/{account.id}/inboxes' do context 'when it is an unauthenticated user' do @@ -15,11 +17,11 @@ RSpec.describe 'Inboxes API', type: :request do context 'when it is an authenticated user' do let(:agent) { create(:user, account: account, role: :agent) } let(:admin) { create(:user, account: account, role: :administrator) } + let(:inbox) { create(:inbox, account: account) } before do create(:inbox, account: account) - second_inbox = create(:inbox, account: account) - create(:inbox_member, user: agent, inbox: second_inbox) + create(:inbox_member, user: agent, inbox: inbox) end it 'returns all inboxes of current_account as administrator' do @@ -54,9 +56,6 @@ RSpec.describe 'Inboxes API', type: :request do end context 'when it is an authenticated user' do - let(:agent) { create(:user, account: account, role: :agent) } - let(:admin) { create(:user, account: account, role: :administrator) } - before do create(:inbox_member, user: agent, inbox: inbox) end @@ -92,7 +91,9 @@ RSpec.describe 'Inboxes API', type: :request do let!(:campaign) { create(:campaign, account: account, inbox: inbox) } it 'returns unauthorized for agents' do - get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/campaigns" + get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/campaigns", + headers: agent.create_new_auth_token, + as: :json expect(response).to have_http_status(:unauthorized) end @@ -263,6 +264,42 @@ RSpec.describe 'Inboxes API', type: :request do end end + describe 'GET /api/v1/accounts/{account.id}/inboxes/{inbox.id}/agent_bot' do + let(:inbox) { create(:inbox, account: account) } + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/agent_bot" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'returns empty when no agent bot is present' do + get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/agent_bot", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + inbox_data = JSON.parse(response.body, symbolize_names: true) + expect(inbox_data[:agent_bot].blank?).to eq(true) + end + + it 'returns the agent bot attached to the inbox' do + agent_bot = create(:agent_bot) + create(:agent_bot_inbox, agent_bot: agent_bot, inbox: inbox) + get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/agent_bot", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + inbox_data = JSON.parse(response.body, symbolize_names: true) + expect(inbox_data[:agent_bot][:name]).to eq agent_bot.name + end + end + end + describe 'POST /api/v1/accounts/{account.id}/inboxes/:id/set_agent_bot' do let(:inbox) { create(:inbox, account: account) } let(:agent_bot) { create(:agent_bot) } diff --git a/spec/controllers/api/v1/agent_bots_controller_spec.rb b/spec/controllers/api/v1/agent_bots_controller_spec.rb deleted file mode 100644 index 258fad5c3..000000000 --- a/spec/controllers/api/v1/agent_bots_controller_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'rails_helper' - -RSpec.describe 'Profile API', type: :request do - let!(:agent_bot1) { create(:agent_bot) } - let!(:agent_bot2) { create(:agent_bot) } - - describe 'GET /api/v1/agent_bots' do - it 'returns all the agent bots in the system' do - get '/api/v1/agent_bots', - as: :json - - expect(response).to have_http_status(:success) - expect(response.body).to include(agent_bot1.name) - expect(response.body).to include(agent_bot2.name) - end - end -end diff --git a/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb b/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb new file mode 100644 index 000000000..d56e1dfb9 --- /dev/null +++ b/spec/controllers/platform/api/v1/agent_bots_controller_spec.rb @@ -0,0 +1,172 @@ +require 'rails_helper' + +RSpec.describe 'Platform Agent Bot API', type: :request do + let!(:agent_bot) { create(:agent_bot) } + + describe 'GET /platform/api/v1/agent_bots' do + context 'when it is an unauthenticated platform app' do + it 'returns unauthorized' do + get '/platform/api/v1/agent_bots' + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an invalid platform app token' do + it 'returns unauthorized' do + get '/platform/api/v1/agent_bots', headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated platform app' do + let(:platform_app) { create(:platform_app) } + + it 'returns unauthorized when its not a permissible object' do + get '/platform/api/v1/agent_bots', headers: { api_access_token: platform_app.access_token.token }, as: :json + expect(response).to have_http_status(:success) + data = JSON.parse(response.body) + expect(data.length).to eq(0) + end + + it 'shows a agent_bot when its permissible object' do + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + + get '/platform/api/v1/agent_bots', + headers: { api_access_token: platform_app.access_token.token }, as: :json + + expect(response).to have_http_status(:success) + data = JSON.parse(response.body) + expect(data.length).to eq(1) + end + end + end + + describe 'GET /platform/api/v1/agent_bots/{agent_bot_id}' do + context 'when it is an unauthenticated platform app' do + it 'returns unauthorized' do + get "/platform/api/v1/agent_bots/#{agent_bot.id}" + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an invalid platform app token' do + it 'returns unauthorized' do + get "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated platform app' do + let(:platform_app) { create(:platform_app) } + + it 'returns unauthorized when its not a permissible object' do + get "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: platform_app.access_token.token }, as: :json + expect(response).to have_http_status(:unauthorized) + end + + it 'shows a agent_bot when its permissible object' do + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + + get "/platform/api/v1/agent_bots/#{agent_bot.id}", + headers: { api_access_token: platform_app.access_token.token }, as: :json + + expect(response).to have_http_status(:success) + data = JSON.parse(response.body) + expect(data['name']).to eq(agent_bot.name) + end + end + end + + describe 'POST /platform/api/v1/agent_bots/' do + context 'when it is an unauthenticated platform app' do + it 'returns unauthorized' do + post '/platform/api/v1/agent_bots' + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an invalid platform app token' do + it 'returns unauthorized' do + post '/platform/api/v1/agent_bots/', headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated platform app' do + let(:platform_app) { create(:platform_app) } + + it 'creates a new agent bot' do + post '/platform/api/v1/agent_bots/', params: { name: 'test' }, + headers: { api_access_token: platform_app.access_token.token }, as: :json + + expect(response).to have_http_status(:success) + data = JSON.parse(response.body) + expect(data['name']).to eq('test') + expect(platform_app.platform_app_permissibles.first.permissible_id).to eq data['id'] + end + end + end + + describe 'PATCH /platform/api/v1/agent_bots/{agent_bot_id}' do + context 'when it is an unauthenticated platform app' do + it 'returns unauthorized' do + patch "/platform/api/v1/agent_bots/#{agent_bot.id}" + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an invalid platform app token' do + it 'returns unauthorized' do + patch "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated platform app' do + let(:platform_app) { create(:platform_app) } + + it 'returns unauthorized when its not a permissible object' do + patch "/platform/api/v1/agent_bots/#{agent_bot.id}", params: { name: 'test' }, + headers: { api_access_token: platform_app.access_token.token }, as: :json + expect(response).to have_http_status(:unauthorized) + end + + it 'updates the agent_bot' do + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + patch "/platform/api/v1/agent_bots/#{agent_bot.id}", params: { name: 'test123' }, + headers: { api_access_token: platform_app.access_token.token }, as: :json + + expect(response).to have_http_status(:success) + data = JSON.parse(response.body) + expect(data['name']).to eq('test123') + end + end + end + + describe 'DELETE /platform/api/v1/agent_bots/{agent_bot_id}' do + context 'when it is an unauthenticated platform app' do + it 'returns unauthorized' do + delete "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated platform app' do + let(:platform_app) { create(:platform_app) } + + it 'returns unauthorized when its not a permissible object' do + delete "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: 'invalid' }, as: :json + expect(response).to have_http_status(:unauthorized) + end + + it 'returns deletes the account user' do + create(:platform_app_permissible, platform_app: platform_app, permissible: agent_bot) + + delete "/platform/api/v1/agent_bots/#{agent_bot.id}", headers: { api_access_token: platform_app.access_token.token }, as: :json + + expect(response).to have_http_status(:success) + expect(AgentBot.count).to eq 0 + end + end + end +end From 2c42e706377517fe2cfaa2cc441952418ce20877 Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Wed, 2 Jun 2021 10:20:40 +0530 Subject: [PATCH 23/67] feat: Add support for canned response command anywhere on rich text editor (#2356) --- .../components/widgets/WootWriter/Editor.vue | 75 ++++++++++++++++--- .../widgets/conversation/ReplyBox.vue | 10 ++- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue index ba26ef177..ff9ddfd67 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/Editor.vue @@ -5,29 +5,38 @@ :search-key="mentionSearchKey" @click="insertMentionNode" /> +
+ + diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue index 90d6f7012..93d965421 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue @@ -55,53 +55,7 @@ -
-
-
-

- {{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.TITLE') }} -

-

{{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.NOTE') }}

-
-
- - - - {{ $t('PROFILE_SETTINGS.FORM.PASSWORD_SECTION.BTN_TEXT') }} - -
-
-
+
@@ -123,10 +77,12 @@ import { mapGetters } from 'vuex'; import { clearCookiesOnLogout } from '../../../../store/utils/api'; import NotificationSettings from './NotificationSettings'; import alertMixin from 'shared/mixins/alertMixin'; +import ChangePassword from './ChangePassword.vue'; export default { components: { NotificationSettings, + ChangePassword, }, mixins: [alertMixin], data() { @@ -136,10 +92,8 @@ export default { name: '', displayName: '', email: '', - password: '', - passwordConfirmation: '', isProfileUpdating: false, - isPasswordChanging: false, + errorMessage: '', }; }, validations: { @@ -152,18 +106,6 @@ export default { required, email, }, - password: { - minLength: minLength(6), - }, - passwordConfirmation: { - minLength: minLength(6), - isEqPassword(value) { - if (value !== this.password) { - return false; - } - return true; - }, - }, }, computed: { ...mapGetters({ @@ -190,41 +132,36 @@ export default { this.avatarUrl = this.currentUser.avatar_url; this.displayName = this.currentUser.display_name; }, - async updateUser(type) { + async updateUser() { this.$v.$touch(); if (this.$v.$invalid) { this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR')); return; } - if (type === 'profile') { - this.isProfileUpdating = true; - } else if (type === 'password') { - this.isPasswordChanging = true; - } + + this.isProfileUpdating = true; const hasEmailChanged = this.currentUser.email !== this.email; try { await this.$store.dispatch('updateProfile', { name: this.name, email: this.email, avatar: this.avatarFile, - password: this.password, displayName: this.displayName, - password_confirmation: this.passwordConfirmation, }); this.isProfileUpdating = false; - this.isPasswordChanging = false; if (hasEmailChanged) { clearCookiesOnLogout(); - this.showAlert(this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')); - } - if (type === 'profile') { - this.showAlert(this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS')); - } else if (type === 'password') { - this.showAlert(this.$t('PROFILE_SETTINGS.PASSWORD_UPDATE_SUCCESS')); + this.errorMessage = this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED'); } + this.errorMessage = this.$t('PROFILE_SETTINGS.UPDATE_SUCCESS'); } catch (error) { + this.errorMessage = this.$t('RESET_PASSWORD.API.ERROR_MESSAGE'); + if (error?.response?.data?.error) { + this.errorMessage = error.response.data.error; + } + } finally { this.isProfileUpdating = false; - this.isPasswordChanging = false; + this.showAlert(this.errorMessage); } }, handleImageUpload({ file, url }) { diff --git a/app/javascript/dashboard/store/modules/auth.js b/app/javascript/dashboard/store/modules/auth.js index 3cdc045d1..1ed23a41a 100644 --- a/app/javascript/dashboard/store/modules/auth.js +++ b/app/javascript/dashboard/store/modules/auth.js @@ -102,12 +102,13 @@ export const actions = { }, updateProfile: async ({ commit }, params) => { + // eslint-disable-next-line no-useless-catch try { const response = await authAPI.profileUpdate(params); setUser(response.data, getHeaderExpiry(response)); commit(types.default.SET_CURRENT_USER); } catch (error) { - // Ignore error + throw error; } }, diff --git a/spec/controllers/api/v1/profiles_controller_spec.rb b/spec/controllers/api/v1/profiles_controller_spec.rb index 4282b02a7..0a936c6d4 100644 --- a/spec/controllers/api/v1/profiles_controller_spec.rb +++ b/spec/controllers/api/v1/profiles_controller_spec.rb @@ -39,7 +39,7 @@ RSpec.describe 'Profile API', type: :request do end context 'when it is an authenticated user' do - let(:agent) { create(:user, account: account, role: :agent) } + let(:agent) { create(:user, password: 'Test123!', account: account, role: :agent) } it 'updates the name & email' do new_email = Faker::Internet.email @@ -56,13 +56,23 @@ RSpec.describe 'Profile API', type: :request do expect(agent.email).to eq(new_email) end - it 'updates the password' do + it 'updates the password when current password is provided' do put '/api/v1/profile', - params: { profile: { password: 'test123', password_confirmation: 'test123' } }, + params: { profile: { current_password: 'Test123!', password: 'test123', password_confirmation: 'test123' } }, headers: agent.create_new_auth_token, as: :json expect(response).to have_http_status(:success) + expect(agent.reload.valid_password?('test123')).to eq true + end + + it 'throws error when current password provided is invalid' do + put '/api/v1/profile', + params: { profile: { current_password: 'Test', password: 'test123', password_confirmation: 'test123' } }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:unprocessable_entity) end it 'updates avatar' do From 49be9e21c3c4f6e24c83dcbb41136127b03e8d83 Mon Sep 17 00:00:00 2001 From: Ani Ravi <5902976+aniravi24@users.noreply.github.com> Date: Wed, 2 Jun 2021 08:46:45 -0700 Subject: [PATCH 25/67] chore: Universal Linking for Android (#2324) * android assetlinks.json config * try renaming html to json * update env example * chore: fix styling * chore: styling part 2 * test: add spec for android assetlinks * minor fixes * minor fixes * remove android sha256 value from env example * add default sh256 value Co-authored-by: Muhsin Keloth Co-authored-by: Sojan Jose --- .env.example | 4 ++++ app/controllers/android_app_controller.rb | 5 +++++ app/views/android_app/assetlinks.json.erb | 12 ++++++++++++ config/routes.rb | 1 + spec/controllers/android_assetlinks_spec.rb | 10 ++++++++++ 5 files changed, 32 insertions(+) create mode 100644 app/controllers/android_app_controller.rb create mode 100644 app/views/android_app/assetlinks.json.erb create mode 100644 spec/controllers/android_assetlinks_spec.rb diff --git a/.env.example b/.env.example index 8d03180a8..08e72816e 100644 --- a/.env.example +++ b/.env.example @@ -116,6 +116,10 @@ SLACK_CLIENT_SECRET= ### Change this env variable only if you are using a custom build mobile app ## Mobile app env variables IOS_APP_ID=6C953F3RX2.com.chatwoot.app +ANDROID_BUNDLE_ID=com.chatwoot.app + +# https://developers.google.com/android/guides/client-auth (use keytool to print the fingerprint in the first section) +ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:D4:5D:D4:53:F8:3B:FB:D3:C6:28:64:1D:AA:08:1E:D8 ### Smart App Banner diff --git a/app/controllers/android_app_controller.rb b/app/controllers/android_app_controller.rb new file mode 100644 index 000000000..c403d7756 --- /dev/null +++ b/app/controllers/android_app_controller.rb @@ -0,0 +1,5 @@ +class AndroidAppController < ApplicationController + def assetlinks + render layout: false + end +end diff --git a/app/views/android_app/assetlinks.json.erb b/app/views/android_app/assetlinks.json.erb new file mode 100644 index 000000000..575ca3b56 --- /dev/null +++ b/app/views/android_app/assetlinks.json.erb @@ -0,0 +1,12 @@ +[ + { + "relation": ["delegate_permission/common.handle_all_urls"], + "target": { + "namespace": "android_app", + "package_name": "<%= ENV['ANDROID_BUNDLE_ID'] %>", + "sha256_cert_fingerprints": [ + "<%= ENV['ANDROID_SHA256_CERT_FINGERPRINT'] %>" + ] + } + } +] \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 499588e42..667346c02 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -232,6 +232,7 @@ Rails.application.routes.draw do # ---------------------------------------------------------------------- # Routes for external service verifications get 'apple-app-site-association' => 'apple_app#site_association' + get '.well-known/assetlinks.json' => 'android_app#assetlinks' # ---------------------------------------------------------------------- # Internal Monitoring Routes diff --git a/spec/controllers/android_assetlinks_spec.rb b/spec/controllers/android_assetlinks_spec.rb new file mode 100644 index 000000000..a44ef111a --- /dev/null +++ b/spec/controllers/android_assetlinks_spec.rb @@ -0,0 +1,10 @@ +require 'rails_helper' + +describe '.well-known/assetlinks.json', type: :request do + describe 'GET /.well-known/assetlinks.json' do + it 'successfully retrieves assetlinks.json file' do + get '/.well-known/assetlinks.json' + expect(response).to have_http_status(:success) + end + end +end From b8aaf6c90d145314aa2f65021e81bde96bf60c71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Jun 2021 21:21:20 +0530 Subject: [PATCH 26/67] chore: Bump dns-packet from 1.3.1 to 1.3.4 (#2346) Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4. - [Release notes](https://github.com/mafintosh/dns-packet/releases) - [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md) - [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pranav Raj S --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 83d1be5ab..3b6fcada0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5667,9 +5667,9 @@ dns-equal@^1.0.0: integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + version "1.3.4" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" + integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" From 75fe851345bf84061a60b48c0988f23a93370ae3 Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Wed, 2 Jun 2021 21:29:53 +0530 Subject: [PATCH 27/67] fix: Update the translation key for Custom Attributes (#2364) --- .../routes/dashboard/conversation/ContactCustomAttributes.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/dashboard/routes/dashboard/conversation/ContactCustomAttributes.vue b/app/javascript/dashboard/routes/dashboard/conversation/ContactCustomAttributes.vue index 2a5e391c2..ee14a9761 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/ContactCustomAttributes.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/ContactCustomAttributes.vue @@ -1,7 +1,7 @@ @@ -153,11 +155,16 @@ export default { this.selectedInbox.name }`; }, - deleteMessage() { + confirmDeleteMessage() { return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.MESSAGE')} ${ this.selectedInbox.name } ?`; }, + confirmPlaceHolderText() { + return `${this.$t('INBOX_MGMT.DELETE.CONFIRM.PLACE_HOLDER', { + inboxName: this.selectedInbox.name, + })}`; + }, }, methods: { openSettings(inbox) { diff --git a/app/javascript/dashboard/routes/dashboard/settings/teams/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/teams/Index.vue index 655898ee3..91d8d4fa5 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/teams/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/teams/Index.vue @@ -63,15 +63,16 @@ />
- - @@ -105,11 +106,16 @@ export default { deleteRejectText() { return this.$t('TEAMS_SETTINGS.DELETE.CONFIRM.NO'); }, - deleteTitle() { + confirmDeleteTitle() { return this.$t('TEAMS_SETTINGS.DELETE.CONFIRM.TITLE', { teamName: this.selectedTeam.name, }); }, + confirmPlaceHolderText() { + return `${this.$t('TEAMS_SETTINGS.DELETE.CONFIRM.PLACE_HOLDER', { + teamName: this.selectedTeam.name, + })}`; + }, }, methods: { async deleteTeam({ id }) { From c028f30f82a3d29ebdb97617478cf82f7aa520eb Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Fri, 4 Jun 2021 21:54:06 +0530 Subject: [PATCH 35/67] chore: Add a different color for messages from bot (#2374) Co-authored-by: Muhsin Keloth Co-authored-by: Pranav Raj S --- .../components/widgets/conversation/Message.vue | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/javascript/dashboard/components/widgets/conversation/Message.vue b/app/javascript/dashboard/components/widgets/conversation/Message.vue index bf39087cf..78b1c148f 100644 --- a/app/javascript/dashboard/components/widgets/conversation/Message.vue +++ b/app/javascript/dashboard/components/widgets/conversation/Message.vue @@ -197,11 +197,16 @@ export default { 'is-private': this.data.private, 'is-image': this.hasImageAttachment, 'is-text': this.hasText, + 'is-from-bot': this.isSentByBot, }; }, isPending() { return this.data.status === MESSAGE_STATUS.PROGRESS; }, + isSentByBot() { + if (this.isPending) return false; + return !this.sender.type || this.sender.type === 'bot'; + }, }, }; @@ -250,6 +255,13 @@ export default { color: var(--color-body); text-decoration: underline; } + + &.is-from-bot { + background: var(--v-400); + .message-text--metadata .time { + color: var(--v-50); + } + } } &.is-pending { From c6487877bf58e4c58d7993bd64e63f679ce0fa2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Jun 2021 18:58:59 +0530 Subject: [PATCH 36/67] chore: Bump ws from 6.2.1 to 6.2.2 (#2382) Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/commits) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3b6fcada0..f2b766a26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15461,9 +15461,9 @@ write@1.0.3: mkdirp "^0.5.1" ws@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" From 14b51e108ac57fd7106621fd5473fb56b81bc058 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Sun, 6 Jun 2021 16:59:05 +0530 Subject: [PATCH 37/67] feat: Add Integration hooks UI (#2301) --- app/javascript/dashboard/api/integrations.js | 8 + .../dashboard/api/specs/apiSpecHelper.js | 1 + .../dashboard/api/specs/integrations.spec.js | 55 +++++++ .../dashboard/assets/scss/_formulate.scss | 18 ++ .../dashboard/assets/scss/_woot.scss | 2 +- app/javascript/dashboard/helper/commons.js | 12 ++ .../dashboard/i18n/default-sidebar.js | 10 ++ .../dashboard/i18n/locale/en/index.js | 2 + .../i18n/locale/en/integrationApps.json | 62 +++++++ .../dashboard/i18n/locale/en/settings.json | 1 + .../settings/integrationapps/Index.vue | 56 +++++++ .../integrationapps/IntegrationHooks.vue | 155 ++++++++++++++++++ .../integrationapps/IntegrationItem.vue | 78 +++++++++ .../MultipleIntegrationHooks.vue | 106 ++++++++++++ .../settings/integrationapps/NewHook.vue | 136 +++++++++++++++ .../SingleIntegrationHooks.vue | 47 ++++++ .../settings/integrationapps/hookMixin.js | 10 ++ .../integrationapps/integrations.routes.js | 43 +++++ .../integrationapps/specs/hookMixin.spec.js | 26 +++ .../dashboard/settings/settings.routes.js | 2 + .../dashboard/store/modules/inboxes.js | 5 + .../dashboard/store/modules/integrations.js | 52 +++++- .../modules/specs/inboxes/getters.spec.js | 5 + .../specs/integrations/actions.spec.js | 81 ++++++--- .../specs/integrations/getters.spec.js | 27 +++ .../specs/integrations/mutations.spec.js | 59 ++++++- .../dashboard/store/mutation-types.js | 2 + app/javascript/packs/application.js | 8 +- app/models/integrations/app.rb | 4 +- app/views/api/v1/models/_hook.json.jbuilder | 2 +- config/integration/apps.yml | 31 +++- config/locales/en.yml | 5 +- package.json | 1 + .../images/integrations/fullcontact.png | Bin 0 -> 7204 bytes yarn.lock | 27 ++- 35 files changed, 1108 insertions(+), 31 deletions(-) create mode 100644 app/javascript/dashboard/api/specs/integrations.spec.js create mode 100644 app/javascript/dashboard/assets/scss/_formulate.scss create mode 100644 app/javascript/dashboard/i18n/locale/en/integrationApps.json create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationHooks.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationItem.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/MultipleIntegrationHooks.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/NewHook.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/SingleIntegrationHooks.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/hookMixin.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/integrations.routes.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrationapps/specs/hookMixin.spec.js create mode 100644 public/dashboard/images/integrations/fullcontact.png diff --git a/app/javascript/dashboard/api/integrations.js b/app/javascript/dashboard/api/integrations.js index 587c94d43..72e433c25 100644 --- a/app/javascript/dashboard/api/integrations.js +++ b/app/javascript/dashboard/api/integrations.js @@ -16,6 +16,14 @@ class IntegrationsAPI extends ApiClient { delete(integrationId) { return axios.delete(`${this.baseUrl()}/integrations/${integrationId}`); } + + createHook(hookData) { + return axios.post(`${this.baseUrl()}/integrations/hooks`, hookData); + } + + deleteHook(hookId) { + return axios.delete(`${this.baseUrl()}/integrations/hooks/${hookId}`); + } } export default new IntegrationsAPI(); diff --git a/app/javascript/dashboard/api/specs/apiSpecHelper.js b/app/javascript/dashboard/api/specs/apiSpecHelper.js index aedea13f8..aab90b045 100644 --- a/app/javascript/dashboard/api/specs/apiSpecHelper.js +++ b/app/javascript/dashboard/api/specs/apiSpecHelper.js @@ -5,6 +5,7 @@ function apiSpecHelper() { post: jest.fn(() => Promise.resolve()), get: jest.fn(() => Promise.resolve()), patch: jest.fn(() => Promise.resolve()), + delete: jest.fn(() => Promise.resolve()), }; window.axios = this.axiosMock; }); diff --git a/app/javascript/dashboard/api/specs/integrations.spec.js b/app/javascript/dashboard/api/specs/integrations.spec.js new file mode 100644 index 000000000..05391ceb6 --- /dev/null +++ b/app/javascript/dashboard/api/specs/integrations.spec.js @@ -0,0 +1,55 @@ +import integrationAPI from '../integrations'; +import ApiClient from '../ApiClient'; +import describeWithAPIMock from './apiSpecHelper'; + +describe('#integrationAPI', () => { + it('creates correct instance', () => { + expect(integrationAPI).toBeInstanceOf(ApiClient); + expect(integrationAPI).toHaveProperty('get'); + expect(integrationAPI).toHaveProperty('show'); + expect(integrationAPI).toHaveProperty('create'); + expect(integrationAPI).toHaveProperty('update'); + expect(integrationAPI).toHaveProperty('delete'); + expect(integrationAPI).toHaveProperty('connectSlack'); + expect(integrationAPI).toHaveProperty('createHook'); + expect(integrationAPI).toHaveProperty('deleteHook'); + }); + describeWithAPIMock('API calls', context => { + it('#connectSlack', () => { + const code = 'SDNFJNSDFNDSJN'; + integrationAPI.connectSlack(code); + expect(context.axiosMock.post).toHaveBeenCalledWith( + '/api/v1/integrations/slack', + { + code, + } + ); + }); + + it('#delete', () => { + integrationAPI.delete(2); + expect(context.axiosMock.delete).toHaveBeenCalledWith( + '/api/v1/integrations/2' + ); + }); + + it('#createHook', () => { + const hookData = { + app_id: 'fullcontact', + settings: { api_key: 'SDFSDGSVE' }, + }; + integrationAPI.createHook(hookData); + expect(context.axiosMock.post).toHaveBeenCalledWith( + '/api/v1/integrations/hooks', + hookData + ); + }); + + it('#deleteHook', () => { + integrationAPI.deleteHook(2); + expect(context.axiosMock.delete).toHaveBeenCalledWith( + '/api/v1/integrations/hooks/2' + ); + }); + }); +}); diff --git a/app/javascript/dashboard/assets/scss/_formulate.scss b/app/javascript/dashboard/assets/scss/_formulate.scss new file mode 100644 index 000000000..57c43a3d6 --- /dev/null +++ b/app/javascript/dashboard/assets/scss/_formulate.scss @@ -0,0 +1,18 @@ +@import '~dashboard/assets/scss/variables'; + +.formulate-input { + .formulate-input-errors { + list-style-type: none; + margin: 0; + padding: 0; + } + + .formulate-input-error { + color: var(--r-400); + display: block; + font-size: var(--font-size-small); + font-weight: $font-weight-normal; + margin-bottom: $space-one; + width: 100%; + } +} diff --git a/app/javascript/dashboard/assets/scss/_woot.scss b/app/javascript/dashboard/assets/scss/_woot.scss index 1712e256e..d156f77b9 100644 --- a/app/javascript/dashboard/assets/scss/_woot.scss +++ b/app/javascript/dashboard/assets/scss/_woot.scss @@ -11,13 +11,13 @@ @import 'mixins'; @import 'foundation-settings'; @import 'helper-classes'; +@import 'formulate'; @import 'foundation-sites/scss/foundation'; @import '~bourbon/core/bourbon'; @include foundation-everything($flex: true); - @import 'typography'; @import 'layout'; @import 'animations'; diff --git a/app/javascript/dashboard/helper/commons.js b/app/javascript/dashboard/helper/commons.js index d7d34283a..40ff06c83 100644 --- a/app/javascript/dashboard/helper/commons.js +++ b/app/javascript/dashboard/helper/commons.js @@ -13,6 +13,18 @@ export default () => { } }; +export const isEmptyObject = obj => + Object.keys(obj).length === 0 && obj.constructor === Object; + +export const isJSONValid = value => { + try { + JSON.parse(value); + } catch (e) { + return false; + } + return true; +}; + export const getTypingUsersText = (users = []) => { const count = users.length; if (count === 1) { diff --git a/app/javascript/dashboard/i18n/default-sidebar.js b/app/javascript/dashboard/i18n/default-sidebar.js index 6488c022e..8bb78f61b 100644 --- a/app/javascript/dashboard/i18n/default-sidebar.js +++ b/app/javascript/dashboard/i18n/default-sidebar.js @@ -74,6 +74,9 @@ export const getSidebarItems = accountId => ({ 'settings_integrations', 'settings_integrations_webhook', 'settings_integrations_integration', + 'settings_applications', + 'settings_applications_webhook', + 'settings_applications_integration', 'general_settings', 'general_settings_index', 'settings_teams_list', @@ -136,6 +139,13 @@ export const getSidebarItems = accountId => ({ toState: frontendURL(`accounts/${accountId}/settings/integrations`), toStateName: 'settings_integrations', }, + settings_applications: { + icon: 'ion-asterisk', + label: 'APPLICATIONS', + hasSubMenu: false, + toState: frontendURL(`accounts/${accountId}/settings/applications`), + toStateName: 'settings_applications', + }, general_settings_index: { icon: 'ion-gear-a', label: 'ACCOUNT_SETTINGS', diff --git a/app/javascript/dashboard/i18n/locale/en/index.js b/app/javascript/dashboard/i18n/locale/en/index.js index cf1dda7fe..4afba79aa 100644 --- a/app/javascript/dashboard/i18n/locale/en/index.js +++ b/app/javascript/dashboard/i18n/locale/en/index.js @@ -15,6 +15,7 @@ import { default as _setNewPassword } from './setNewPassword.json'; import { default as _settings } from './settings.json'; import { default as _signup } from './signup.json'; import { default as _teamsSettings } from './teamsSettings.json'; +import { default as _integrationApps } from './integrationApps.json'; export default { ..._agentMgmt, @@ -34,4 +35,5 @@ export default { ..._settings, ..._signup, ..._teamsSettings, + ..._integrationApps, }; diff --git a/app/javascript/dashboard/i18n/locale/en/integrationApps.json b/app/javascript/dashboard/i18n/locale/en/integrationApps.json new file mode 100644 index 000000000..a80ecb837 --- /dev/null +++ b/app/javascript/dashboard/i18n/locale/en/integrationApps.json @@ -0,0 +1,62 @@ +{ + "INTEGRATION_APPS": { + "FETCHING": "Fetching Integrations", + "NO_HOOK_CONFIGURED": "There are no %{integrationId} integrations configured in this account.", + "HEADER": "Applications", + "STATUS": { + "ENABLED": "Enabled", + "DISABLED": "Disabled" + }, + "CONFIGURE": "Configure", + "ADD_BUTTON": "Add a new hook", + "DELETE": { + "TITLE": { + "INBOX": "Confirm deletion", + "ACCOUNT": "Disconnect" + }, + "MESSAGE": { + "INBOX": "Are you sure to delete?", + "ACCOUNT": "Are you sure to disconnect?" + }, + "CONFIRM_BUTTON_TEXT": { + "INBOX": "Yes, Delete", + "ACCOUNT": "Yes, Disconnect" + }, + "CANCEL_BUTTON_TEXT": "Cancel", + "API": { + "SUCCESS_MESSAGE": "Hook deleted successfully", + "ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later" + } + }, + "LIST": { + "FETCHING": "Fetching integration hooks", + "INBOX": "Inbox", + "DELETE": { + "BUTTON_TEXT": "Delete" + } + }, + "ADD": { + "FORM": { + "INBOX": { + "LABEL": "Select Inbox", + "PLACEHOLDER": "Select Inbox" + }, + "SUBMIT": "Create", + "CANCEL": "Cancel" + }, + "API": { + "SUCCESS_MESSAGE": "Integration hook added successfully", + "ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later" + } + }, + "CONNECT": { + "BUTTON_TEXT": "Connect" + }, + "DISCONNECT": { + "BUTTON_TEXT": "Disconnect" + }, + "SIDEBAR_DESCRIPTION": { + "DIALOGFLOW": "Dialogflow is a natural language understanding platform that makes it easy to design and integrate a conversational user interface into your mobile app, web application, device, bot, interactive voice response system, and so on.

Dialogflow integration with %{installationName} allows you to configure a Dialogflow bot with your inboxes which lets the bot handle the queries initially and hand them over to an agent when needed. Dialogflow can be used to qualifying the leads, reduce the workload of agents by providing frequently asked questions etc.

To add Dialogflow, you need to create a Service Account in your Google project console and share the credentials. Please refer to the Dialogflow docs for more information." + } + } +} diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json index aa51c35be..c7bb0cb6d 100644 --- a/app/javascript/dashboard/i18n/locale/en/settings.json +++ b/app/javascript/dashboard/i18n/locale/en/settings.json @@ -133,6 +133,7 @@ "CANNED_RESPONSES": "Canned Responses", "INTEGRATIONS": "Integrations", "ACCOUNT_SETTINGS": "Account Settings", + "APPLICATIONS": "Applications", "LABELS": "Labels", "TEAMS": "Teams" }, diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/Index.vue new file mode 100644 index 000000000..5441742bd --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/Index.vue @@ -0,0 +1,56 @@ + + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationHooks.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationHooks.vue new file mode 100644 index 000000000..6d9c4395c --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationHooks.vue @@ -0,0 +1,155 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationItem.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationItem.vue new file mode 100644 index 000000000..7d0e64550 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/IntegrationItem.vue @@ -0,0 +1,78 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/MultipleIntegrationHooks.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/MultipleIntegrationHooks.vue new file mode 100644 index 000000000..fdce8cadc --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/MultipleIntegrationHooks.vue @@ -0,0 +1,106 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/NewHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/NewHook.vue new file mode 100644 index 000000000..3d343ab14 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/NewHook.vue @@ -0,0 +1,136 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/SingleIntegrationHooks.vue b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/SingleIntegrationHooks.vue new file mode 100644 index 000000000..24d9f2610 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/SingleIntegrationHooks.vue @@ -0,0 +1,47 @@ + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/hookMixin.js b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/hookMixin.js new file mode 100644 index 000000000..fd545f480 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/hookMixin.js @@ -0,0 +1,10 @@ +export default { + computed: { + isHookTypeInbox() { + return this.integration.hook_type === 'inbox'; + }, + hasConnectedHooks() { + return !!this.integration.hooks.length; + }, + }, +}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/integrations.routes.js b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/integrations.routes.js new file mode 100644 index 000000000..c05982551 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/integrations.routes.js @@ -0,0 +1,43 @@ +import Index from './Index'; +import SettingsContent from '../Wrapper'; +import IntegrationHooks from './IntegrationHooks'; +import { frontendURL } from '../../../../helper/URLHelper'; + +export default { + routes: [ + { + path: frontendURL('accounts/:accountId/settings/applications'), + component: SettingsContent, + props: params => { + const showBackButton = params.name !== 'settings_applications'; + const backUrl = + params.name === 'settings_applications_integration' + ? { name: 'settings_applications' } + : ''; + return { + headerTitle: 'INTEGRATION_APPS.HEADER', + icon: 'ion-asterisk', + showBackButton, + backUrl, + }; + }, + children: [ + { + path: '', + name: 'settings_applications', + component: Index, + roles: ['administrator'], + }, + { + path: ':integration_id', + name: 'settings_applications_integration', + component: IntegrationHooks, + roles: ['administrator'], + props: route => ({ + integrationId: route.params.integration_id, + }), + }, + ], + }, + ], +}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrationapps/specs/hookMixin.spec.js b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/specs/hookMixin.spec.js new file mode 100644 index 000000000..eb895ccf3 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrationapps/specs/hookMixin.spec.js @@ -0,0 +1,26 @@ +import { shallowMount } from '@vue/test-utils'; +import hookMixin from '../hookMixin'; + +describe('hookMixin', () => { + const Component = { + render() {}, + mixins: [hookMixin], + data() { + return { + integration: { + hook_type: 'inbox', + hooks: [{ id: 1, properties: {} }], + }, + }; + }, + }; + const wrapper = shallowMount(Component); + + it('#isHookTypeInbox returns correct value', () => { + expect(wrapper.vm.isHookTypeInbox).toBe(true); + }); + + it('#hasConnectedHooks returns correct value', () => { + expect(wrapper.vm.hasConnectedHooks).toBe(true); + }); +}); diff --git a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js index 46617349c..6293f08c1 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js +++ b/app/javascript/dashboard/routes/dashboard/settings/settings.routes.js @@ -4,6 +4,7 @@ import agent from './agents/agent.routes'; import canned from './canned/canned.routes'; import inbox from './inbox/inbox.routes'; import integrations from './integrations/integrations.routes'; +import integrationapps from './integrationapps/integrations.routes'; import labels from './labels/labels.routes'; import profile from './profile/profile.routes'; import reports from './reports/reports.routes'; @@ -32,5 +33,6 @@ export default { ...profile.routes, ...reports.routes, ...teams.routes, + ...integrationapps.routes, ], }; diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js index 753622ccd..c4f38c96e 100644 --- a/app/javascript/dashboard/store/modules/inboxes.js +++ b/app/javascript/dashboard/store/modules/inboxes.js @@ -65,6 +65,11 @@ export const getters = { getUIFlags($state) { return $state.uiFlags; }, + getWebsiteInboxes($state) { + return $state.records.filter( + item => item.channel_type === 'Channel::WebWidget' + ); + }, }; export const actions = { diff --git a/app/javascript/dashboard/store/modules/integrations.js b/app/javascript/dashboard/store/modules/integrations.js index 7c8d62ef8..6b3ebe8a4 100644 --- a/app/javascript/dashboard/store/modules/integrations.js +++ b/app/javascript/dashboard/store/modules/integrations.js @@ -1,4 +1,5 @@ /* eslint no-param-reassign: 0 */ +import Vue from 'vue'; import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers'; import * as types from '../mutation-types'; import IntegrationsAPI from '../../api/integrations'; @@ -6,15 +7,23 @@ import IntegrationsAPI from '../../api/integrations'; const state = { records: [], uiFlags: { + isCreating: false, isFetching: false, isFetchingItem: false, isUpdating: false, + isCreatingHook: false, + isDeletingHook: false, }, }; export const getters = { getIntegrations($state) { - return $state.records; + return $state.records.filter( + item => item.id !== 'fullcontact' && item.id !== 'dialogflow' + ); + }, + getAppIntegrations($state) { + return $state.records.filter(item => item.id === 'dialogflow'); }, getIntegration: $state => integrationId => { const [integration] = $state.records.filter( @@ -63,6 +72,28 @@ export const actions = { commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isDeleting: false }); } }, + createHook: async ({ commit }, hookData) => { + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: true }); + try { + const response = await IntegrationsAPI.createHook(hookData); + commit(types.default.ADD_INTEGRATION_HOOKS, response.data); + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: false }); + } catch (error) { + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: false }); + throw new Error(error); + } + }, + deleteHook: async ({ commit }, { appId, hookId }) => { + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: true }); + try { + await IntegrationsAPI.deleteHook(hookId); + commit(types.default.DELETE_INTEGRATION_HOOKS, { appId, hookId }); + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: false }); + } catch (error) { + commit(types.default.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: false }); + throw new Error(error); + } + }, }; export const mutations = { @@ -72,6 +103,25 @@ export const mutations = { [types.default.SET_INTEGRATIONS]: MutationHelpers.set, [types.default.ADD_INTEGRATION]: MutationHelpers.updateAttributes, [types.default.DELETE_INTEGRATION]: MutationHelpers.updateAttributes, + [types.default.ADD_INTEGRATION_HOOKS]: ($state, data) => { + $state.records.forEach((element, index) => { + if (element.id === data.app_id) { + const record = $state.records[index]; + Vue.set(record, 'hooks', [...record.hooks, data]); + } + }); + }, + [types.default.DELETE_INTEGRATION_HOOKS]: ($state, { appId, hookId }) => { + $state.records.forEach((element, index) => { + if (element.id === appId) { + const record = $state.records[index]; + const hooksWithoutDeletedHook = record.hooks.filter( + hook => hook.id !== hookId + ); + Vue.set(record, 'hooks', hooksWithoutDeletedHook); + } + }); + }, }; export default { diff --git a/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js b/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js index 5bd531a80..f3e85773c 100644 --- a/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/inboxes/getters.spec.js @@ -9,6 +9,11 @@ describe('#getters', () => { expect(getters.getInboxes(state)).toEqual(inboxList); }); + it('getWebsiteInboxes', () => { + const state = { records: inboxList }; + expect(getters.getWebsiteInboxes(state).length).toEqual(3); + }); + it('getInbox', () => { const state = { records: inboxList, diff --git a/app/javascript/dashboard/store/modules/specs/integrations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/integrations/actions.spec.js index cc0dd9d22..1f625b0c4 100644 --- a/app/javascript/dashboard/store/modules/specs/integrations/actions.spec.js +++ b/app/javascript/dashboard/store/modules/specs/integrations/actions.spec.js @@ -1,29 +1,30 @@ import axios from 'axios'; import { actions } from '../../integrations'; -import * as types from '../../../mutation-types'; +import types from '../../../mutation-types'; import integrationsList from './fixtures'; const commit = jest.fn(); global.axios = axios; jest.mock('axios'); +const errorMessage = { message: 'Incorrect header' }; describe('#actions', () => { describe('#get', () => { it('sends correct actions if API is success', async () => { axios.get.mockResolvedValue({ data: integrationsList }); await actions.get({ commit }); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isFetching: true }], - [types.default.SET_INTEGRATIONS, integrationsList.payload], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isFetching: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isFetching: true }], + [types.SET_INTEGRATIONS, integrationsList.payload], + [types.SET_INTEGRATIONS_UI_FLAG, { isFetching: false }], ]); }); it('sends correct actions if API is error', async () => { - axios.get.mockRejectedValue({ message: 'Incorrect header' }); + axios.get.mockRejectedValue(errorMessage); await actions.get({ commit }); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isFetching: true }], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isFetching: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isFetching: true }], + [types.SET_INTEGRATIONS_UI_FLAG, { isFetching: false }], ]); }); }); @@ -34,17 +35,17 @@ describe('#actions', () => { axios.post.mockResolvedValue({ data: data }); await actions.connectSlack({ commit }); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isUpdating: true }], - [types.default.ADD_INTEGRATION, data], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isUpdating: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isUpdating: true }], + [types.ADD_INTEGRATION, data], + [types.SET_INTEGRATIONS_UI_FLAG, { isUpdating: false }], ]); }); it('sends correct actions if API is error', async () => { - axios.post.mockRejectedValue({ message: 'Incorrect header' }); + axios.post.mockRejectedValue(errorMessage); await actions.connectSlack({ commit }); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isUpdating: true }], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isUpdating: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isUpdating: true }], + [types.SET_INTEGRATIONS_UI_FLAG, { isUpdating: false }], ]); }); }); @@ -55,17 +56,59 @@ describe('#actions', () => { axios.delete.mockResolvedValue({ data: data }); await actions.deleteIntegration({ commit }, data.id); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isDeleting: true }], - [types.default.DELETE_INTEGRATION, data], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isDeleting: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeleting: true }], + [types.DELETE_INTEGRATION, data], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeleting: false }], ]); }); it('sends correct actions if API is error', async () => { - axios.delete.mockRejectedValue({ message: 'Incorrect header' }); + axios.delete.mockRejectedValue(errorMessage); await actions.deleteIntegration({ commit }); expect(commit.mock.calls).toEqual([ - [types.default.SET_INTEGRATIONS_UI_FLAG, { isDeleting: true }], - [types.default.SET_INTEGRATIONS_UI_FLAG, { isDeleting: false }], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeleting: true }], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeleting: false }], + ]); + }); + }); + + describe('#createHooks', () => { + it('sends correct actions if API is success', async () => { + let data = { id: 'slack', enabled: false }; + axios.post.mockResolvedValue({ data: data }); + await actions.createHook({ commit }, data); + expect(commit.mock.calls).toEqual([ + [types.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: true }], + [types.ADD_INTEGRATION_HOOKS, data], + [types.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.post.mockRejectedValue(errorMessage); + await expect(actions.createHook({ commit })).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: true }], + [types.SET_INTEGRATIONS_UI_FLAG, { isCreatingHook: false }], + ]); + }); + }); + + describe('#deleteHook', () => { + it('sends correct actions if API is success', async () => { + let data = { appId: 'dialogflow', hookId: 2 }; + axios.delete.mockResolvedValue({ data }); + await actions.deleteHook({ commit }, data); + expect(commit.mock.calls).toEqual([ + [types.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: true }], + [types.DELETE_INTEGRATION_HOOKS, { appId: 'dialogflow', hookId: 2 }], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: false }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.delete.mockRejectedValue(errorMessage); + await expect(actions.deleteHook({ commit }, {})).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([ + [types.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: true }], + [types.SET_INTEGRATIONS_UI_FLAG, { isDeletingHook: false }], ]); }); }); diff --git a/app/javascript/dashboard/store/modules/specs/integrations/getters.spec.js b/app/javascript/dashboard/store/modules/specs/integrations/getters.spec.js index 7ec4657a6..2c4ef9e63 100644 --- a/app/javascript/dashboard/store/modules/specs/integrations/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/integrations/getters.spec.js @@ -34,6 +34,33 @@ describe('#getters', () => { ]); }); + it('getAppIntegrations', () => { + const state = { + records: [ + { + id: 1, + name: 'test1', + logo: 'test', + enabled: true, + }, + { + id: 'dialogflow', + name: 'test2', + logo: 'test', + enabled: true, + }, + ], + }; + expect(getters.getAppIntegrations(state)).toEqual([ + { + id: 'dialogflow', + name: 'test2', + logo: 'test', + enabled: true, + }, + ]); + }); + it('getUIFlags', () => { const state = { uiFlags: { diff --git a/app/javascript/dashboard/store/modules/specs/integrations/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/integrations/mutations.spec.js index c485b5018..212ef44ce 100644 --- a/app/javascript/dashboard/store/modules/specs/integrations/mutations.spec.js +++ b/app/javascript/dashboard/store/modules/specs/integrations/mutations.spec.js @@ -1,11 +1,11 @@ -import * as types from '../../../mutation-types'; +import types from '../../../mutation-types'; import { mutations } from '../../integrations'; describe('#mutations', () => { describe('#GET_INTEGRATIONS', () => { it('set integrations records', () => { const state = { records: [] }; - mutations[types.default.SET_INTEGRATIONS](state, [ + mutations[types.SET_INTEGRATIONS](state, [ { id: 1, name: 'test1', @@ -23,4 +23,59 @@ describe('#mutations', () => { ]); }); }); + + describe('#ADD_INTEGRATION_HOOKS', () => { + it('set integrations hook records', () => { + const state = { records: [{ id: 'dialogflow', hooks: [] }] }; + const hookRecord = { + id: 1, + app_id: 'dialogflow', + status: false, + inbox: { id: 1, name: 'Chatwoot' }, + account_id: 1, + hook_type: 'inbox', + settings: { project_id: 'test', credentials: {} }, + }; + mutations[types.ADD_INTEGRATION_HOOKS](state, hookRecord); + expect(state.records).toEqual([ + { + id: 'dialogflow', + hooks: [hookRecord], + }, + ]); + }); + }); + + describe('#DELETE_INTEGRATION_HOOKS', () => { + it('delete integrations hook record', () => { + const state = { + records: [ + { + id: 'dialogflow', + hooks: [ + { + id: 1, + app_id: 'dialogflow', + status: false, + inbox: { id: 1, name: 'Chatwoot' }, + account_id: 1, + hook_type: 'inbox', + settings: { project_id: 'test', credentials: {} }, + }, + ], + }, + ], + }; + mutations[types.DELETE_INTEGRATION_HOOKS](state, { + appId: 'dialogflow', + hookId: 1, + }); + expect(state.records).toEqual([ + { + id: 'dialogflow', + hooks: [], + }, + ]); + }); + }); }); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 29b1e45b5..ec9f527c4 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -83,6 +83,8 @@ export default { SET_INTEGRATIONS: 'SET_INTEGRATIONS', ADD_INTEGRATION: 'ADD_INTEGRATION', DELETE_INTEGRATION: 'DELETE_INTEGRATION', + ADD_INTEGRATION_HOOKS: 'ADD_INTEGRATION_HOOKS', + DELETE_INTEGRATION_HOOKS: 'DELETE_INTEGRATION_HOOKS', // WebHook SET_WEBHOOK_UI_FLAG: 'SET_WEBHOOK_UI_FLAG', diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index e0443c15c..2b7b3cc30 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -10,6 +10,7 @@ import axios from 'axios'; // Global Components import hljs from 'highlight.js'; import Multiselect from 'vue-multiselect'; +import VueFormulate from '@braid/vue-formulate'; import WootSwitch from 'components/ui/Switch'; import WootWizard from 'components/ui/Wizard'; import { sync } from 'vuex-router-sync'; @@ -19,7 +20,7 @@ import WootUiKit from '../dashboard/components'; import App from '../dashboard/App'; import i18n from '../dashboard/i18n'; import createAxios from '../dashboard/helper/APIHelper'; -import commonHelpers from '../dashboard/helper/commons'; +import commonHelpers, { isJSONValid } from '../dashboard/helper/commons'; import { getAlertAudio } from '../shared/helpers/AudioNotificationHelper'; import { initFaviconSwitcher } from '../shared/helpers/faviconHelper'; import router from '../dashboard/routes'; @@ -48,6 +49,11 @@ Vue.use(VueRouter); Vue.use(VueI18n); Vue.use(WootUiKit); Vue.use(Vuelidate); +Vue.use(VueFormulate, { + rules: { + JSON: ({ value }) => isJSONValid(value), + }, +}); Vue.use(VTooltip, { defaultHtml: false, }); diff --git a/app/models/integrations/app.rb b/app/models/integrations/app.rb index bae1ca02f..149a305b3 100644 --- a/app/models/integrations/app.rb +++ b/app/models/integrations/app.rb @@ -38,8 +38,8 @@ class Integrations::App case params[:id] when 'slack' ENV['SLACK_CLIENT_SECRET'].present? - when 'dialogflow' - false + when 'dialogflow', 'fullcontact' + true else true end diff --git a/app/views/api/v1/models/_hook.json.jbuilder b/app/views/api/v1/models/_hook.json.jbuilder index e9c921516..3c692c13d 100644 --- a/app/views/api/v1/models/_hook.json.jbuilder +++ b/app/views/api/v1/models/_hook.json.jbuilder @@ -1,7 +1,7 @@ json.id resource.id json.app_id resource.app_id json.status resource.enabled? -json.inbox_id resource.inbox_id +json.inbox resource.inbox&.slice(:id, :name) json.account_id resource.account_id json.hook_type resource.hook_type json.settings resource.settings diff --git a/config/integration/apps.yml b/config/integration/apps.yml index 1c6276894..a9ae7b4a2 100644 --- a/config/integration/apps.yml +++ b/config/integration/apps.yml @@ -3,7 +3,7 @@ # logo: place the image in /public/dashboard/images/integrations and reference here # i18n_key: the key under which translations for the integration is placed in en.yml # action: if integration requires external redirect url -# hook_type: ( account / inbox ) +# hook_type: ( account / inbox ) # allow_multiple_hooks: whether multiple hooks can be created for the integration # settings_json_schema: the json schema used to validate the settings hash (https://json-schema.org/) # settings_form_schema: the formulate schema used in frontend to render settings form (https://vueformulate.com/) @@ -43,11 +43,38 @@ dialogflow: { "label": "Dialogflow Project ID", "type": "text", - "name": "project_id" + "name": "project_id", + "validation": "required", + "validationName": 'Project Id', }, { "label": "Dialogflow Project Key File", "type": "textarea", "name": "credentials", + "validation": "required|JSON", + "validationName": 'Credentials', + "validation-messages": { + "JSON": "Invalid JSON", + "required": "Credentials is required" + } } ] + visible_properties: ['project_id'] +fullcontact: + id: fullcontact + logo: fullcontact.png + i18n_key: fullcontact + action: /fullcontact + hook_type: account + allow_multiple_hooks: false + settings_json_schema: + { + 'type': 'object', + 'properties': { 'api_key': { 'type': 'string' } }, + 'required': ['api_key'], + 'additionalProperties': false, + } + settings_form_schema: + [{ 'label': 'API Key', 'type': 'text', 'name': 'api_key',"validation": "required", }] + visible_properties: ['api_key'] + diff --git a/config/locales/en.yml b/config/locales/en.yml index ecea2c545..b4bd4357c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -95,4 +95,7 @@ en: description: "Webhook events provide you the realtime information about what's happening in your account. You can make use of the webhooks to communicate the events to your favourite apps like Slack or Github. Click on Configure to set up your webhooks." dialogflow: name: "Dialogflow" - description: "Connect your Dialogflow bot to your inbox. Let the bots handle the queries before handing it off to the customer service agent." + description: "Build chatbots using Dialogflow and connect them to your inbox quickly. Let the bots handle the queries before handing them off to a customer service agent." + fullcontact: + name: "Fullcontact" + description: "FullContact integration helps to enrich visitor profiles. Identify the users as soon as they share their email address and offer them tailored customer service. Connect your FullContact to your account by sharing the FullContact API Key." diff --git a/package.json b/package.json index 8bbad0b4d..cb6b4fa42 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "build-storybook": "build-storybook" }, "dependencies": { + "@braid/vue-formulate": "^2.5.2", "@chatwoot/prosemirror-schema": "https://github.com/chatwoot/prosemirror-schema.git#7e8acadd10d7b932c0dc0bd0a18f804434f83517", "@chatwoot/utils": "^0.0.3", "@rails/actioncable": "6.1.3", diff --git a/public/dashboard/images/integrations/fullcontact.png b/public/dashboard/images/integrations/fullcontact.png new file mode 100644 index 0000000000000000000000000000000000000000..1e5d9b7f9e5c2b5bece94a6a9cae344f4d2f912b GIT binary patch literal 7204 zcmZXZWmFVS*!JmWcS&hjO1hC{34x_cq+^lpmPTp;Sz1aOq?VKvq;m-orKP(=N=gZN z{6FvW?LBAiGr#MaYv#j!=F6Ok)zMZVC88t3z`!6?Q&rT(z`zFlS2)=J93@xH(0>z- zgS?hJ21dg>;(IIHe;U(US49D%c8uZRpPe!${Sb1m4CG=&%;;T(pWH|ZtYT)J*+G&&(?HH&76ZweuYSy<;`I4{NVl4k!ZdUAKpz$*c+LdSYnzhJqMD| zPctt0Jwjf${giQkP$K$X&>&HvLAEtruDI}e(Pu@Q(;2fwJ@aqu`-dd7h~8BRJ=@sY zM`?ddle>lE5lLK@{u*I=n~&fY{I%!&xke|zMm?#GwE-#%)cljeD^)Y0YQsjMQk7qD zMm(MXhgW@&ql#9sv(^~JsS=Xrp>Tf6q*CGrI+xeO$|v#PF6BOFp)H_=JF54l$yeiO zh%BoaYZ3a0II1rPMH@x46;zw<#H5d2t@fTN7Px-c`cxFBSc9YG^qcWfqd_cz)Pmuv zNz9WnHYo!yi8X~1WI^iTT8@q8MKe?yw?idJD!-HkE!7X>yV*IWu~{vh`D^k36x}R0 zW1RhSixC@7K~!Tg%F;sm**V1#MeTBmy2aH1MIRx3iWmZ`3Rcj|ew{ATSXHZvoKH!G z<>`ub+^=X7Bqty6l782Ac(lWw2(2muU`p2?T{v&Ej0*qIndntkeV=noM6=n{49UAG zhVSQ8Db64v-8qMfysv0xd2nC@#g*i`+XfKP}EF1?=?XNoc4H38Qg8E?j{der`&k?V^y;%t=&+LgD!LWGR2@e z{IXjz`LL1<3=*LqJ~LqWOjhKo)ibeHwDJL z_O|pp^_14OS~Y%Ik^ZyU(kD^h^qJXXu&wa#`%7tj-gvvH_4m%L(v-(yo4wiUEu~}R zpE9j#R@KWmH0>7>y1HBB-s<%ZviV-h$i66Eqy=$4Zuigy#?`cKFRp0Ntyh^CbX3`+ zrfS`k`anMo>bUVJ7Y(Kntl*qD45c;Sj~GH(ZP@u?(NPsHP#yLtKQXR$dcxCw{`lB3 z*c4$Asce$pTHDxt361=e{9ZmQxM@u*1(X9|AS|+uWd+Yjc?J+@;S&Li4toF3K9HOA z`{C9ahTN7NFfPvpgT)vj4i_GkpptVn~V#RtuOLH0i1uQPpUtxV+zu$uKAESoYaV)ESO>;Yt@yoRzMKO22z;? z|It8#!*(kit#U&stB)dciClF&51O2Jt^O_t&U;kZg9*ODPCk8aRq53N11Hv*_oh5d zeta;9&062X-WBW`{!0rOs26Dx6Wokmf3yM2F3H`95%q=Rvl@Gp(2A1;uqcq}Jt0O5 zUEfmgvrzcK#DEPs&N}v_O)~9FG9->H{bJ@zUTNf*;^mPB+9>21kA@!(tEn2N9aI4 zUc$j?>`|GQiNDxDuGhjdh}qe9C#-AgY_~HInl~`>k&+jj?L@=I|9(JS)t*ODvm(FM z(C}4wR%J3bt}pkCMv)RmW?^&v#P{L(IjqCDc<*gE7(}(KOHfnK+X<_J{to!i{8_pAcF8z zP76>KJ0;w}s0=|nW_;sHi@R_v0I;0kJeDL1c~2XQ7zr@49Gsxwm{q>36a$zIW5G}d zj32kcX(YYaL0oe|gVUIsVCi&Txn=>81q@dLQX4KCm=yzt#`h3W)mP2hq6abSw3yoq zWW(~I%>5>9wZl4S*2~w7K`-Z0hcWB*j!jwcE@|n@Gjkg=k+fL&a*H=#M$w=MSX!rC zOkElw4xSvUz$+6ei-#ZB0o@P)%76gNFL+)tQPc7a$oR&$NjF(Seo{OkI6o9e_OWu>pG3*S0U3ZxK%@B+y{YloimPrg%S2rybPRd2#!;atLM087_0 z5Jh%cJEw3SV#P=P4#Zr-5@s=HHfVUAfvd16L%?jt8eloLJ0%7~`n(&_&QxuQP*Ey7 zTgjYWsrh&NiFVSPJdX8h0gv3lzsI}F{~ix>&XgyJj4f$CUb!@~o%Z+=@TTQ?d0k=` z>EIAL@i396qUJCWx7A_rV-X?^H$C#DD9Kw$!SBfZ0emATfzMvA$+8ZfK6Z(%?Hbim zi#H5eFm>0qZa)h{7}t=Y%;8TVB(id12L-0BBYWMVZWo&u&)y(f?QPWe%7)}6O@^-L zNq_yaJaEr@bnuo}#@0IdHBs-|O)=~q>th0M)&^9LMxyJ)%9e4h2Mo%}H(V}5AffWA zt%G7)bYsoaaLGXWVe-Ca4Ak@|J#yZ*y+tS$SVO|>ja@_Pr}*R(Hj?bGK5c?_t!}}@ zTNBpSBT8)~CqCLbt>e-*JktUKW04~H>dp0OxkIL9OB{RjdPST9)888r_B=EFc8hj` zV|8SE$)Kt7WwV#<2hHS$jGuR)@l#rNRoQ7GSH>ps_44UCh%ZZhu(KuzYV=aavL=)- z|F-}61D?xthg|V4xdh=iynYg=wIB0Oz|V6IRZD65NnKyIQxlk1ps0K-hOAADoHxdl z$2PglL?~<@Lf>98`M%pu2(N+j{4SF`zWd6UnzpKYAxc@);~Bfiu`xW+>1Kv3J=)@H zED4S@cBiz5PmNlEm7mCHZ{&DSl(=Qq1uQgD+4m3JzqJ$u<3tu^ zy>+pLi@&IANG_yD!WZSf(ac1U{d{&gs9V=+nVO;T`5GLR^U9MhCJ)SA9^v9M=ERag zROUMv7umPLB>6`8D4w_*NvB-|tW}kJ8k$QYh1V z`nvYz{q#^stLt|=DN;#08=F|q_Zc-5R>g(ECg-POHE0!`*BcCRQ-%IHaYhwy>kCf_ z+HbI&+M^wYUf&R?6#x1rr8Q`G)O^+n_uibq8Z{ifTA<$TgB>{Sm&8uiet)7ps3qx@ zg?Jmm0hkI^$}1?hTyRFhN`67M|3Nt$3=rRV{t#g7@?*GYI2H}cedh-e(|BWLEqhlzL+_SRtBbhj!UyAM! z8IY)-L071^jppAPMtjE$Il|G*#Eq4ZUeA3ww>(Awc;~X&6E>}SeU6v<2)RUb$h8Z$fBTYX;zgh~SGp(8MAwGN3n0w12x?`+Z*BV>*e%Hq zps-AvBu9oTf4GG2ps93A2En{mKPERUlXnMyJs>@aA}*I7V7zZZE*T%3k1`4Y*|Lxe zq%DGd>tBOaw@&0IlWpMy7tQ7}3XsJ|uG@$wNYC_s`kW(PPMygH6Q!@c^ z5&&#+$P7t?kKGlhqAzr%`0|DhPT}n>Wn%!$xlMQLzT+S?#qmq*mMdQT^=1gE${-it zTl*T1XQo)+V10Sg(#%CJSRYC)9?^4xz`}m;bVfN3!mMJn0@{dX(Ry)u% ztYxWiH;*^iFOAzwi9{Jhm-pMm_g8Rthv__)HAxV~3!(Yc>}A>dt5ms!%PT6`G1iR+ z8Wlqqi~#!j4l}`-ATnMDetez8&r%xkX1IDsv^>`Jx3nFuU3SsL^D8yJAz-(a(S5t4 z@1}irB@dKT##QkN76L1mI|xWq<1gOkW-e?ZYB9j|jQNcg!5tkA`#$~P)X>UmfoS8y zlpa8m%#z_J2dd7;w-shzPyCvsvj@$WpXow{c+kdTfO0&I(twH~E`)I1G%za1J#co$E5<*D<&FGW82V% z8wv7E*tkpLWZJ~nXA`h2hwg@(O|VqR;x8r4GvvFW*Xr8JcDk|ECc zP{*qRvOGu`ht9%65=XrJeRYea^qmVpGN34o8IokBIc2CJ2Em}xoBrp?l+|wbPS`l> zNXy2$^UvQ4kl)3jnHyB+rG+*y8bRVE5Fz6j;z2~8f@c|Nh+lxAQC z6hFNKv@&&$h%t;r(%TFoirV_FmR%v5)0)T+M;?jJ($=X5y<7mC)L3=Cm35shy5!_a z*{5#~nHT{{YjtsJ>T8P1$jCnhh7Jcz`0Xh0gdfW6jkf~Ba*q|&c{loTNh_rVLvQxYVGlKQ-*~%(5 zliSq|hK&Fa(A$MH86UkR5BYZ0ZKgC^a}fZ_IDAK%ip;w)zF_4{abI40TrU*Cte*NF z5Ig?k&u@de>M2s7&1Ph*0jIS=`vMHPEng)9t|rgM2ajK-NY2v=!-)(z?G2oBTm*nx zlEELGMDZ`q2yb1x#?a;h0GmUda$6?6%owBjlu+~g9Lo>x>w||q^kP8zPOdk zW`-;z_=(z__ZdXbR^l&G+4c+O2{mp$vPhF+jwb|$H}(uhj#*wLe*&g0Ab^stB|(_Q zizwvFH?CE zlHVAq$v61Ci}LpU6PsLNf7-uv9vu?7vvv10i{joF7d8SNE^g2(Tg$Z!=842p=7|Ht z#z|t2A>WJB@1hgY?El@}ysTtmUoWi*_(3Qgy2bR#H3fgq{=6HmG>A^{ z1*^n%rT6E&P!!ANnOOGOrXb4>QkH!*C+9xOI^h1=DS7@hrYblrXozy#d44dC`xYm_ z<@1j=H$iF0@JlS>`4AAW(@arX)5QN)hA%BpTAKZ%**EpG4cD!Oq5s0+%WR>6`y^erzw#9jK9}#x@n8WEWiuYSucaB*!E44mIc84=O~DzfwTc9 z=8}vFewD?8Hf3yzl01%1!hOIql|uU!*oOUV)Z8SU*0zwd){Giz9x!K24|*M$k_g(H zsc8`H(X^Iu!r@ZeazIFC=2fERht!%b55 z3USjL`;qr@rK|8RZmw7i#23rl0C;>88=^v3GMaVD9j6$_cm25VI|u9<*IIrgZC1AP z+0&+_Co$a4vD6BYk*Uc#ue&%TT>~u;zKAb68aq>tA!%`Lr?U9sp*v4ucP{#_#ENeh z8(HGD{VRTUv}j<`mHip{^Qt9&kMDFbi}kwcJu?YuMwhyJK+JoAYRU*Z#211$#KhPG z@85LTE(r0*h|NUn3l*KM+d1M7KaX#asi zU_IB2xRr|)(N3SWTP*=I3~$FOQ77kVxFqd6oewg%AhZ9a=c_vPa|^9QzbtE>jT1g% zup2XdB=l7?<`S%zHl{eFwJZuwLiH8seBcj^;qT2$P^uvAjebj;g$z+p`?4~wHkSob zkKicM?vr-?2dH;o?2CG3E<(SkcD!jz-fsee{~_yzoYAdiQRRh8 zhJmrJHyr9qdvRmf+oY;XpJ@y5561kZwz?4kamm<<5%t7+(!i+^YU_@eKDy#lmd}-I0u3y>RnP$1{0`p%oV{aENo#Ep4W7FY0N7t_4G@$wdm!*BQ z8tO`RtteQ55f+Na*g&=<=x#C=Ej4gOrH5G*Xx$_DLilCbuL%4~=Va^BuRx|+$R^j! zqH#bUw&R&XO`^#+>B|H0y5Vn;$r>XNQ(s15;6u|(doRyJTwrO#C}d7lFd&(1I8?(j z_bYY@N_4h8s{SX%52pbDA=w_CSLl8hIrj#9>sZj>BTsgq92%-Y+hQ%Q$@=S!uojwYaFc-YsFl9CMS{-1eB%MmbWtX5w2L}gq8KQE&e5FaMPT$YF=(C z5(MdFxUO(Q=JYdZ^l!G9w{r^T{6x*E z?BQKp=VeDSc{JA_#ie1IMPsMNNYTgdln#r6919C8vqdij%oJN_gYz)1*l9+QN*{3Uac#tZ~DIdiS1aG0qTS( z`xAu^6;|O?)l98fa63A6a&y|=!s0!(l$z~F!4~Xo?f+5{`U&_ef*3kZ2F}syE@dY; z3wO$5dM$%RTtH{@C02?Z`I>6)>8jcHR;EAN{lf3TQSA&fc(IUwtNFQxE7l`IHO}T{ z)&(Z({}*g#B;u9cFRphJc&4_*` zQ81Kkz|^#a%mP5cOp(VL{yqwjpJxYSiRGS-xp;12qW^Eh1R-YjzXZ*+{8JoTsAIHe zKBE}y9;z;t^UsC*Wi%ZV9q_FTtAsp+9~2UM-{{a~PP}cCV%H9wAVmCk_DM2yS6<>K z1h$idw{rY73()>>$D6b=U3e_(Pbz!tE<_i+xYFw*Hq)WYnipi0;LrtIcF=x8!&LmL zN_KYw^vT~Yz|cIVm1Pp-vDPfccmIN3gJfXA9$GdD+T+ZDqR&BgRoWz=bJSs+Dl8am z{fZ?pMnDlR)xeNq zF#N)lwx_7>OtPt7H0dEGk0l zgN>q7KVn$8|7IPensD|Uf+Mf!C`NO!26wUq74ZT{K5~;TI>$9tQeu#8Os!Lqfv4BW z$gHNBAuM%$C^h-u1~SoFR@k)12$#N57kX#Y6@U8uw) zwodL>eJ70HW#eBlE!AhjxG=+q*7h_ldhVVfwWB{U{3OmN0llwZ@eDHC*LdX6_U2bh zLAAGWNC0}rKA Date: Sun, 6 Jun 2021 16:59:47 +0530 Subject: [PATCH 38/67] feat: Add an option to edit webhook URL (#2316) Co-authored-by: Pranav Raj S --- .../i18n/locale/en/integrations.json | 23 +++- .../settings/integrations/EditWebHook.vue | 108 ++++++++++++++++++ .../integrations/{New.vue => NewWebHook.vue} | 30 +++-- .../settings/integrations/Webhook.vue | 50 ++++++-- .../modules/specs/webhooks/actions.spec.js | 24 ++++ .../modules/specs/webhooks/mutations.spec.js | 10 ++ .../dashboard/store/modules/webhooks.js | 20 +++- .../dashboard/store/mutation-types.js | 1 + 8 files changed, 233 insertions(+), 33 deletions(-) create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue rename app/javascript/dashboard/routes/dashboard/settings/integrations/{New.vue => NewWebHook.vue} (81%) diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index 8e158982a..cbbe0ebbf 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -12,10 +12,25 @@ "LIST": { "404": "There are no webhooks configured for this account.", "TITLE": "Manage webhooks", - "TABLE_HEADER": [ - "Webhook endpoint", - "Actions" - ] + "TABLE_HEADER": ["Webhook endpoint", "Actions"] + }, + "EDIT": { + "BUTTON_TEXT": "Edit", + "TITLE": "Edit webhook", + "CANCEL": "Cancel", + "DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.", + "FORM": { + "END_POINT": { + "LABEL": "Webhook URL", + "PLACEHOLDER": "Example: https://example/api/webhook", + "ERROR": "Please enter a valid URL" + }, + "SUBMIT": "Edit webhook" + }, + "API": { + "SUCCESS_MESSAGE": "Webhook URL updated successfully", + "ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later" + } }, "ADD": { "CANCEL": "Cancel", diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue new file mode 100644 index 000000000..cdfa2998a --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue @@ -0,0 +1,108 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/New.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue similarity index 81% rename from app/javascript/dashboard/routes/dashboard/settings/integrations/New.vue rename to app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue index 55fbbc650..8c445771f 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/New.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue @@ -28,14 +28,15 @@ @@ -45,15 +46,14 @@ diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue index f3e718464..fe0e30861 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue @@ -14,19 +14,17 @@
@@ -111,12 +109,12 @@ export default { .actions { display: flex; visibility: hidden; - } - .button--emoji { - margin-left: var(--space-small); - height: var(--space-medium); - width: var(--space-medium); + .button { + margin-left: var(--space-small); + height: var(--space-medium); + width: var(--space-medium); + } } } diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ReminderItem.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ReminderItem.vue index 2fe47a66e..49755d1d2 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ReminderItem.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ReminderItem.vue @@ -23,20 +23,18 @@
From 77d4f86ec951ad2c631a33f48475ac5754d9fcce Mon Sep 17 00:00:00 2001 From: Nithin David Thomas Date: Wed, 9 Jun 2021 18:25:05 +0530 Subject: [PATCH 50/67] chore: Clean up contact note styles (#2404) --- .../contacts/components/ContactNote.stories.js | 2 +- .../dashboard/contacts/components/ContactNote.vue | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.stories.js b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.stories.js index 00d766a49..36d97af1a 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.stories.js +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.stories.js @@ -24,7 +24,7 @@ export default { }, }, timeStamp: { - defaultValue: '1618046084', + defaultValue: 1618046084, control: { type: 'number', }, diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue index fe0e30861..715b4e375 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactNote.vue @@ -6,7 +6,7 @@