diff --git a/.github/dashboard-screen.png b/.github/dashboard-screen.png deleted file mode 100644 index 847ae1582..000000000 Binary files a/.github/dashboard-screen.png and /dev/null differ diff --git a/.github/screenshots/dashboard-dark.png b/.github/screenshots/dashboard-dark.png new file mode 100644 index 000000000..4d08b52b9 Binary files /dev/null and b/.github/screenshots/dashboard-dark.png differ diff --git a/.github/screenshots/dashboard.png b/.github/screenshots/dashboard.png new file mode 100644 index 000000000..b8b99be49 Binary files /dev/null and b/.github/screenshots/dashboard.png differ diff --git a/.github/screenshots/header-dark.png b/.github/screenshots/header-dark.png new file mode 100644 index 000000000..84931aee3 Binary files /dev/null and b/.github/screenshots/header-dark.png differ diff --git a/.github/screenshots/header.png b/.github/screenshots/header.png new file mode 100644 index 000000000..f10ca0faf Binary files /dev/null and b/.github/screenshots/header.png differ diff --git a/Gemfile.lock b/Gemfile.lock index bfe4b1970..9f1ee5eec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -501,14 +501,14 @@ GEM newrelic_rpm (9.6.0) base64 nio4r (2.7.3) - nokogiri (1.18.3) + nokogiri (1.18.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.18.3-arm64-darwin) + nokogiri (1.18.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.3-x86_64-darwin) + nokogiri (1.18.4-x86_64-darwin) racc (~> 1.4) - nokogiri (1.18.3-x86_64-linux-gnu) + nokogiri (1.18.4-x86_64-linux-gnu) racc (~> 1.4) oauth (1.1.0) oauth-tty (~> 1.0, >= 1.0.1) diff --git a/README.md b/README.md index f09bd5698..21316b422 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,11 @@ -## 🚨 Note: This branch is unstable. For the stable branch's source code, please use the branch [3.x](https://github.com/chatwoot/chatwoot/tree/3.x) - - -Chat dashboard dark mode -Chat dashboard +Header light mode +Header dark mode ___ # Chatwoot -Customer engagement suite, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc. -

- - Deploy - - - Deploy to DO - -

+The modern customer support platform, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc.

Maintainability @@ -31,41 +20,71 @@ Customer engagement suite, an open-source alternative to Intercom, Zendesk, Sale Artifact HUB

-Chat dashboard dark mode -Chat dashboard -Chatwoot is an open-source, self-hosted customer engagement suite. Chatwoot lets you view and manage your customer data, communicate with them irrespective of which medium they use, and re-engage them based on their profile. +

+ + Deploy + + + Deploy to DO + +

-## Features +Chat dashboard dark mode +Chat dashboard -Chatwoot supports the following conversation channels: +--- - - **Website**: Talk to your customers using our live chat widget and make use of our SDK to identify a user and provide contextual support. - - **Facebook**: Connect your Facebook pages and start replying to the direct messages to your page. - - **Instagram**: Connect your Instagram profile and start replying to the direct messages. - - **Twitter**: Connect your Twitter profiles and reply to direct messages or the tweets where you are mentioned. - - **Telegram**: Connect your Telegram bot and reply to your customers right from a single dashboard. - - **WhatsApp**: Connect your WhatsApp business account and manage the conversation in Chatwoot. - - **Line**: Connect your Line account and manage the conversations in Chatwoot. - - **SMS**: Connect your Twilio SMS account and reply to the SMS queries in Chatwoot. - - **API Channel**: Build custom communication channels using our API channel. - - **Email**: Forward all your email queries to Chatwoot and view it in our integrated dashboard. +Chatwoot is the modern, open-source, and self-hosted customer support platform designed to help businesses deliver exceptional customer support experience. Built for scale and flexibility, Chatwoot gives you full control over your customer data while providing powerful tools to manage conversations across channels. -And more. +### ✨ Captain – AI Agent for Support -Other features include: +Supercharge your support with Captain, Chatwoot’s AI agent. Captain helps automate responses, handle common queries, and reduce agent workload—ensuring customers get instant, accurate answers. With Captain, your team can focus on complex conversations while routine questions are resolved automatically. Read more about Captain [here](https://chwt.app/captain-docs). + +### 💬 Omnichannel Support Desk + +Chatwoot centralizes all customer conversations into one powerful inbox, no matter where your customers reach out from. It supports live chat on your website, email, Facebook, Instagram, Twitter, WhatsApp, Telegram, Line, SMS etc. + +### 📚 Help center portal + +Publish help articles, FAQs, and guides through the built-in Help Center Portal. Enable customers to find answers on their own, reduce repetitive queries, and keep your support team focused on more complex issues. + +### 🗂️ Other features + +#### Collaboration & Productivity + +- Private Notes and @mentions for internal team discussions. +- Labels to organize and categorize conversations. +- Keyboard Shortcuts and a Command Bar for quick navigation. +- Canned Responses to reply faster to frequently asked questions. +- Auto-Assignment to route conversations based on agent availability. +- Multi-lingual Support to serve customers in multiple languages. +- Custom Views and Filters for better inbox organization. +- Business Hours and Auto-Responders to manage response expectations. +- Teams and Automation tools for scaling support workflows. +- Agent Capacity Management to balance workload across the team. + +#### Customer Data & Segmentation +- Contact Management with profiles and interaction history. +- Contact Segments and Notes for targeted communication. +- Campaigns to proactively engage customers. +- Custom Attributes for storing additional customer data. +- Pre-Chat Forms to collect user information before starting conversations. + +#### Integrations +- Slack Integration to manage conversations directly from Slack. +- Dialogflow Integration for chatbot automation. +- Dashboard Apps to embed internal tools within Chatwoot. +- Shopify Integration to view and manage customer orders right within Chatwoot. +- Use Google Translate to translate messages from your customers in realtime. +- Create and manage Linear tickets within Chatwoot. + +#### Reports & Insights +- Live View of ongoing conversations for real-time monitoring. +- Conversation, Agent, Inbox, Label, and Team Reports for operational visibility. +- CSAT Reports to measure customer satisfaction. +- Downloadable Reports for offline analysis and reporting. -- **CRM**: Save all your customer information right inside Chatwoot, use contact notes to log emails, phone calls, or meeting notes. -- **Custom Attributes**: Define custom attribute attributes to store information about a contact or a conversation and extend the product to match your workflow. -- **Shared multi-brand inboxes**: Manage multiple brands or pages using a shared inbox. -- **Private notes**: Use @mentions and private notes to communicate internally about a conversation. -- **Canned responses (Saved replies)**: Improve the response rate by adding saved replies for frequently asked questions. -- **Conversation Labels**: Use conversation labels to create custom workflows. -- **Auto assignment**: Chatwoot intelligently assigns a ticket to the agents who have access to the inbox depending on their availability and load. -- **Conversation continuity**: If the user has provided an email address through the chat widget, Chatwoot will send an email to the customer under the agent name so that the user can continue the conversation over the email. -- **Multi-lingual support**: Chatwoot supports 10+ languages. -- **Powerful API & Webhooks**: Extend the capability of the software using Chatwoot’s webhooks and APIs. -- **Integrations**: Chatwoot natively integrates with Slack right now. Manage your conversations in Slack without logging into the dashboard. ## Documentation @@ -107,13 +126,11 @@ For other supported options, checkout our [deployment page](https://chatwoot.com Looking to report a vulnerability? Please refer our [SECURITY.md](./SECURITY.md) file. - -## Community? Questions? Support ? +## Community If you need help or just want to hang out, come, say hi on our [Discord](https://discord.gg/cJXdrwS) server. - -## Contributors ✨ +## Contributors Thanks goes to all these [wonderful people](https://www.chatwoot.com/docs/contributors): diff --git a/app/builders/contact_inbox_builder.rb b/app/builders/contact_inbox_builder.rb index 8fcd2b158..ffa45db2e 100644 --- a/app/builders/contact_inbox_builder.rb +++ b/app/builders/contact_inbox_builder.rb @@ -64,5 +64,40 @@ class ContactInboxBuilder inbox_id: @inbox.id, source_id: @source_id ) + rescue ActiveRecord::RecordNotUnique + Rails.logger.info("[ContactInboxBuilder] RecordNotUnique #{@source_id} #{@contact.id} #{@inbox.id}") + update_old_contact_inbox + retry + end + + def update_old_contact_inbox + # The race condition occurs when there’s a contact inbox with the + # same source ID but linked to a different contact. This can happen + # if the agent updates the contact’s email or phone number, or + # if the contact is merged with another. + # + # We update the old contact inbox source_id to a random value to + # avoid disrupting the current flow. However, the root cause of + # this issue is a flaw in the contact inbox model design. + # Contact inbox is essentially tracking a session and is not + # needed for non-live chat channels. + raise ActiveRecord::RecordNotUnique unless allowed_channels? + + contact_inbox = ::ContactInbox.find_by(inbox_id: @inbox.id, source_id: @source_id) + return if contact_inbox.blank? + + contact_inbox.update!(source_id: new_source_id) + end + + def new_source_id + if @inbox.whatsapp? || @inbox.sms? || @inbox.twilio? + "whatsapp:#{@source_id}#{rand(100)}" + else + "#{rand(10)}#{@source_id}" + end + end + + def allowed_channels? + @inbox.email? || @inbox.sms? || @inbox.twilio? || @inbox.whatsapp? end end diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb index 44592c201..31f98e384 100644 --- a/app/finders/conversation_finder.rb +++ b/app/finders/conversation_finder.rb @@ -32,6 +32,7 @@ class ConversationFinder def initialize(current_user, params) @current_user = current_user @current_account = current_user.account + @is_admin = current_account.account_users.find_by(user_id: current_user.id)&.administrator? @params = params end @@ -85,8 +86,13 @@ class ConversationFinder @team = current_account.teams.find(params[:team_id]) if params[:team_id] end + def find_conversation_by_inbox + @conversations = current_account.conversations + @conversations = @conversations.where(inbox_id: @inbox_ids) unless params[:inbox_id].blank? && @is_admin + end + def find_all_conversations - @conversations = current_account.conversations.where(inbox_id: @inbox_ids) + find_conversation_by_inbox filter_by_conversation_type if params[:conversation_type] @conversations end diff --git a/app/helpers/billing_helper.rb b/app/helpers/billing_helper.rb index 26669e38d..e2ada7e86 100644 --- a/app/helpers/billing_helper.rb +++ b/app/helpers/billing_helper.rb @@ -18,4 +18,8 @@ module BillingHelper def non_web_inboxes(account) account.inboxes.where.not(channel_type: Channel::WebWidget.to_s).count end + + def agents(account) + account.users.count + end end diff --git a/app/javascript/dashboard/App.vue b/app/javascript/dashboard/App.vue index ae50055fb..e51958e9e 100644 --- a/app/javascript/dashboard/App.vue +++ b/app/javascript/dashboard/App.vue @@ -4,7 +4,6 @@ import AddAccountModal from '../dashboard/components/layout/sidebarComponents/Ad import LoadingState from './components/widgets/LoadingState.vue'; import NetworkNotification from './components/NetworkNotification.vue'; import UpdateBanner from './components/app/UpdateBanner.vue'; -import UpgradeBanner from './components/app/UpgradeBanner.vue'; import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue'; import PendingEmailVerificationBanner from './components/app/PendingEmailVerificationBanner.vue'; import vueActionCable from './helper/actionCable'; @@ -31,7 +30,6 @@ export default { UpdateBanner, PaymentPendingBanner, WootSnackbarBox, - UpgradeBanner, PendingEmailVerificationBanner, }, setup() { @@ -146,7 +144,6 @@ export default { diff --git a/app/javascript/dashboard/assets/scss/_helper-classes.scss b/app/javascript/dashboard/assets/scss/_helper-classes.scss index 48ee1918b..229606254 100644 --- a/app/javascript/dashboard/assets/scss/_helper-classes.scss +++ b/app/javascript/dashboard/assets/scss/_helper-classes.scss @@ -4,7 +4,6 @@ @apply inline-block h-6 py-0 px-6 relative align-middle w-6; &.message { - @include normal-shadow; @apply bg-white dark:bg-slate-800 rounded-full left-0 my-3 mx-auto p-4 top-0; &::before { diff --git a/app/javascript/dashboard/assets/scss/_mixins.scss b/app/javascript/dashboard/assets/scss/_mixins.scss index 9ca87c859..fc742d24f 100644 --- a/app/javascript/dashboard/assets/scss/_mixins.scss +++ b/app/javascript/dashboard/assets/scss/_mixins.scss @@ -1,79 +1,7 @@ @import 'dashboard/assets/scss/variables'; -@import 'widget/assets/scss/mixins'; $spinner-before-border-color: rgba(255, 255, 255, 0.7); -//borders -@mixin border-nil() { - border-color: transparent; - border: 0; -} - -@mixin thin-border($color) { - border: 1px solid $color; -} - -@mixin custom-border-bottom($size, $color) { - border-bottom: $size solid $color; -} - -@mixin custom-border-top($size, $color) { - border-top: $size solid $color; -} - -@mixin border-normal() { - @apply border border-slate-50 dark:border-slate-700; -} - -@mixin border-normal-left() { - @apply border-l border-slate-50 dark:border-slate-700; -} - -@mixin border-normal-top() { - @apply border-t border-slate-50 dark:border-slate-700; -} - -@mixin border-normal-right() { - @apply border-r border-slate-50 dark:border-slate-700; -} - -@mixin border-normal-bottom() { - @apply border-b border-slate-50 dark:border-slate-700; -} - -@mixin border-light() { - @apply border border-slate-25 dark:border-slate-700; -} - -@mixin border-light-left() { - @apply border-l border-slate-25 dark:border-slate-700; -} - -@mixin border-light-top() { - @apply border-t border-slate-25 dark:border-slate-700; -} - -@mixin border-light-right() { - @apply border-r border-slate-25 dark:border-slate-700; -} - -@mixin border-light-bottom() { - @apply border-b border-slate-25 dark:border-slate-700; -} - -// background -@mixin background-gray() { - background: $color-background; -} - -@mixin background-light() { - @apply bg-slate-50 dark:bg-slate-800; -} - -@mixin background-white() { - @apply bg-white dark:bg-slate-900; -} - // input form @mixin ghost-input() { box-shadow: none; @@ -87,65 +15,6 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7); } } -// flex-layout -@mixin space-between() { - display: flex; - justify-content: space-between; -} - -@mixin space-between-column() { - @include space-between; - flex-direction: column; -} - -@mixin space-between-row() { - @include space-between; - flex-direction: row; -} - -@mixin flex-shrink() { - flex: 0 0 auto; - max-width: 100%; -} - -@mixin flex-weight($value) { - // Grab flex-grow for older browsers. - $flex-grow: nth($value, 1); - - // 2009 - @include prefixer(box-flex, $flex-grow, webkit moz spec); - - // 2011 (IE 10), 2012 - @include prefixer(flex, $value, webkit moz ms spec); -} - -// full height -@mixin full-height() { - height: 100%; -} - -@mixin round-corner() { - border-radius: 1000px; -} - -@mixin scroll-on-hover() { - overflow: hidden; - - &:hover { - overflow-y: auto; - } -} - - -@mixin horizontal-scroll() { - overflow-y: auto; -} - -@mixin elegant-card() { - @include normal-shadow; - border-radius: $space-small; -} - @mixin color-spinner() { @keyframes spinner { to { @@ -230,17 +99,3 @@ $spinner-before-border-color: rgba(255, 255, 255, 0.7); border-left: $size solid transparent; } } - -@mixin text-ellipsis { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -@mixin three-column-grid($column-one-width: 16rem, - $column-three-width: 16rem) { - width: 100%; - height: 100%; - display: grid; - grid-template-columns: minmax($column-one-width, 6fr) 10fr minmax($column-three-width, 6fr); -} diff --git a/app/javascript/dashboard/assets/scss/_rtl.scss b/app/javascript/dashboard/assets/scss/_rtl.scss index 36679fc60..4f5f4b843 100644 --- a/app/javascript/dashboard/assets/scss/_rtl.scss +++ b/app/javascript/dashboard/assets/scss/_rtl.scss @@ -81,28 +81,6 @@ margin-left: var(--space-small); } } - - // Conversation sidebar close button - .close-button--rtl { - transform: rotate(180deg); - } - - // Resolve actions button - .resolve-actions { - .button-group .button:first-child { - border-bottom-left-radius: 0; - border-bottom-right-radius: var(--border-radius-normal); - border-top-left-radius: 0; - border-top-right-radius: var(--border-radius-normal); - } - - .button-group .button:last-child { - border-bottom-left-radius: var(--border-radius-normal); - border-bottom-right-radius: 0; - border-top-left-radius: var(--border-radius-normal); - border-top-right-radius: 0; - } - } } // Conversation list @@ -177,71 +155,6 @@ } } - // Help center - .article-container .row--article-block { - td:last-child { - direction: initial; - } - } - - .portal-popover__container .portal { - .actions-container { - margin-left: unset; - margin-right: var(--space-one); - } - } - - .edit-article--container { - .header-right--wrap { - .button-group .button:first-child { - border-bottom-left-radius: 0; - border-bottom-right-radius: var(--border-radius-normal); - border-top-left-radius: 0; - border-top-right-radius: var(--border-radius-normal); - } - - .button-group .button:last-child { - border-bottom-left-radius: var(--border-radius-normal); - border-bottom-right-radius: 0; - border-top-left-radius: var(--border-radius-normal); - border-top-right-radius: 0; - } - } - - .header-left--wrap { - .back-button { - direction: initial; - } - } - - .article--buttons { - .dropdown-pane { - left: 0; - position: absolute; - right: unset; - } - } - - .sidebar-button { - transform: rotate(180deg); - } - } - - .article-settings--container { - border-left: 0; - border-right: 1px solid var(--color-border-light); - flex-direction: row-reverse; - margin-left: 0; - margin-right: var(--space-normal); - padding-left: 0; - padding-right: var(--space-normal); - } - - .category-list--container .header-left--wrap { - direction: initial; - justify-content: flex-end; - } - // Toggle switch .toggle-button { &.small { @@ -264,11 +177,6 @@ } } - // Widget builder - .widget-builder-container .widget-preview { - direction: initial; - } - // Modal .modal-container { text-align: right; @@ -282,7 +190,6 @@ } // Other changes - .colorpicker--chrome { direction: initial; } @@ -291,14 +198,6 @@ direction: initial; } - .contact--details .contact--bio { - direction: ltr; - } - - .merge-contacts .child-contact-wrap { - direction: ltr; - } - .contact--form .input-group { direction: initial; } diff --git a/app/javascript/dashboard/assets/scss/_woot.scss b/app/javascript/dashboard/assets/scss/_woot.scss index 004d8bf59..622eb4085 100644 --- a/app/javascript/dashboard/assets/scss/_woot.scss +++ b/app/javascript/dashboard/assets/scss/_woot.scss @@ -29,7 +29,6 @@ @import 'rtl'; @import 'widgets/base'; -@import 'widgets/buttons'; @import 'widgets/conversation-view'; @import 'widgets/tabs'; @import 'widgets/woot-tables'; diff --git a/app/javascript/dashboard/assets/scss/widgets/_base.scss b/app/javascript/dashboard/assets/scss/widgets/_base.scss index b2c271173..9c4ccf126 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_base.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_base.scss @@ -40,6 +40,12 @@ dl:not(.reset-base) { @apply mb-0; } +// Button base +button { + font-family: inherit; + @apply inline-block text-center align-middle cursor-pointer text-sm m-0 py-1 px-2.5 transition-all duration-200 ease-in-out border-0 border-none rounded-lg disabled:opacity-50; +} + // Form elements // ------------------------- label { diff --git a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss b/app/javascript/dashboard/assets/scss/widgets/_buttons.scss deleted file mode 100644 index 133e07bd1..000000000 --- a/app/javascript/dashboard/assets/scss/widgets/_buttons.scss +++ /dev/null @@ -1,228 +0,0 @@ -// scss-lint:disable SpaceAfterPropertyColon -// scss-lint:disable MergeableSelector -button { - font-family: inherit; - transition: - background-color 0.25s ease-out, - color 0.25s ease-out; - @apply inline-block items-center mb-0 text-center align-middle cursor-pointer text-sm mt-0 mx-0 py-1 px-2.5 border border-solid border-transparent dark:border-transparent rounded-[0.3125rem]; - - &:disabled, - &.disabled { - @apply opacity-40 cursor-not-allowed; - } -} - -.button-group { - @apply mb-0 flex flex-nowrap items-stretch; - - .button { - flex: 0 0 auto; - @apply m-0 text-sm rounded-none first:rounded-tl-[0.3125rem] first:rounded-bl-[0.3125rem] last:rounded-tr-[0.3125rem] last:rounded-br-[0.3125rem] rtl:space-x-reverse; - } - - .button--only-icon { - @apply w-10 justify-center pl-0 pr-0; - } -} - -.back-button { - @apply m-0; -} - -.button { - @apply items-center bg-n-brand px-2.5 text-white dark:text-white inline-flex h-10 mb-0 gap-2 font-medium; - - .button__content { - @apply w-full whitespace-nowrap overflow-hidden text-ellipsis; - - img, - svg { - @apply inline-block; - } - } - - &:hover:not(:disabled):not(.success):not(.alert):not(.warning):not( - .clear - ):not(.smooth):not(.hollow) { - @apply bg-n-brand/80 dark:bg-n-brand/80; - } - - &:disabled, - &.disabled { - @apply opacity-40 cursor-not-allowed; - } - - &.success { - @apply bg-n-teal-9 text-white dark:text-white; - } - - &.secondary { - @apply bg-n-solid-3 text-white dark:text-white; - } - - &.primary { - @apply bg-n-brand text-white dark:text-white; - } - - &.clear { - @apply text-n-blue-text dark:text-n-blue-text bg-transparent dark:bg-transparent; - } - - &.alert { - @apply bg-n-ruby-9 text-white dark:text-white; - - &.clear { - @apply bg-transparent dark:bg-transparent; - } - } - - &.warning { - @apply bg-n-amber-9 text-white dark:text-white; - - &.clear { - @apply bg-transparent dark:bg-transparent; - } - } - - &.tiny { - @apply h-6 text-[10px]; - } - - &.small { - @apply h-8 text-xs; - } - - .spinner { - @apply px-2 py-0; - } - - // @TODDO - Remove after moving all buttons to woot-button - .icon + .button__content { - @apply w-auto; - } - - &.expanded { - @apply flex justify-center text-center; - } - - &.round { - @apply rounded-full; - } - - // @TODO Use with link - - &.compact { - @apply pb-0 pt-0; - } - - &.hollow { - @apply border border-n-brand/40 bg-transparent text-n-blue-text hover:enabled:bg-n-brand/20; - - &.secondary { - @apply text-n-slate-12 border-n-slate-5 hover:enabled:bg-n-slate-5; - } - - &.success { - @apply text-n-teal-9 border-n-teal-8 hover:enabled:bg-n-teal-5; - } - - &.alert { - @apply text-n-ruby-9 border-n-ruby-8 hover:enabled:bg-n-ruby-5; - } - - &.warning { - @apply text-n-amber-9 border-n-amber-8 hover:enabled:bg-n-amber-5; - } - } - - // Smooth style - &.smooth { - @apply bg-n-brand/10 dark:bg-n-brand/30 text-n-blue-text hover:enabled:bg-n-brand/20 dark:hover:enabled:bg-n-brand/40; - - &.secondary { - @apply bg-n-slate-4 text-n-slate-11 hover:enabled:text-n-slate-11 hover:enabled:bg-n-slate-5; - } - - &.success { - @apply bg-n-teal-4 text-n-teal-11 hover:enabled:text-n-teal-11 hover:enabled:bg-n-teal-5; - } - - &.alert { - @apply bg-n-ruby-4 text-n-ruby-11 hover:enabled:text-n-ruby-11 hover:enabled:bg-n-ruby-5; - } - - &.warning { - @apply bg-n-amber-4 text-n-amber-11 hover:enabled:text-n-amber-11 hover:enabled:bg-n-amber-5; - } - } - - &.clear { - @apply text-n-blue-text hover:enabled:bg-n-brand/10 dark:hover:enabled:bg-n-brand/30; - - &.secondary { - @apply text-n-slate-12 hover:enabled:bg-n-slate-4; - } - - &.success { - @apply text-n-teal-10 hover:enabled:bg-n-teal-4; - } - - &.alert { - @apply text-n-ruby-11 hover:enabled:bg-n-ruby-4; - } - - &.warning { - @apply text-n-amber-11 hover:enabled:bg-n-amber-4; - } - - &:active { - &.secondary { - @apply active:bg-n-slate-3 dark:active:bg-n-slate-7; - } - } - - &:focus { - &.secondary { - @apply focus:bg-n-slate-4 dark:focus:bg-n-slate-6; - } - } - } - - // Sizes - &.tiny { - @apply h-6; - } - - &.small { - @apply h-8 pb-1 pt-1; - } - - &.large { - @apply h-12; - } - - &.button--only-icon { - @apply justify-center pl-0 pr-0 w-10; - - &.tiny { - @apply w-6; - } - - &.small { - @apply w-8; - } - - &.large { - @apply w-12; - } - } - - &.link { - @apply h-auto m-0 p-0; - - &:hover { - @apply underline; - } - } -} diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue index 588f1712e..14d944b3e 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsForm/CreateNewContactDialog.vue @@ -55,13 +55,13 @@ defineExpose({ dialogRef, contactsFormRef, onSuccess }); @click="closeDialog" />