Merge branch 'develop' into fix/cw-4085
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 285 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 966 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 934 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
+4
-4
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2246121/282256557-1570674b-d142-4198-9740-69404cc6a339.png#gh-light-mode-only" width="100%" alt="Chat dashboard dark mode"/>
|
||||
<img src="https://user-images.githubusercontent.com/2246121/282256632-87f6a01b-6467-4e0e-8a93-7bbf66d03a17.png#gh-dark-mode-only" width="100%" alt="Chat dashboard"/>
|
||||
<img src="./.github/screenshots/header.png#gh-light-mode-only" width="100%" alt="Header light mode"/>
|
||||
<img src="./.github/screenshots/header-dark.png#gh-dark-mode-only" width="100%" alt="Header dark mode"/>
|
||||
|
||||
___
|
||||
|
||||
# Chatwoot
|
||||
|
||||
Customer engagement suite, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc.
|
||||
<p>
|
||||
<a href="https://heroku.com/deploy?template=https://github.com/chatwoot/chatwoot/tree/master" alt="Deploy to Heroku">
|
||||
<img width="150" alt="Deploy" src="https://www.herokucdn.com/deploy/button.svg"/>
|
||||
</a>
|
||||
<a href="https://marketplace.digitalocean.com/apps/chatwoot?refcode=f2238426a2a8" alt="Deploy to DigitalOcean">
|
||||
<img width="200" alt="Deploy to DO" src="https://www.deploytodo.com/do-btn-blue.svg"/>
|
||||
</a>
|
||||
</p>
|
||||
The modern customer support platform, an open-source alternative to Intercom, Zendesk, Salesforce Service Cloud etc.
|
||||
|
||||
<p>
|
||||
<a href="https://codeclimate.com/github/chatwoot/chatwoot/maintainability"><img src="https://api.codeclimate.com/v1/badges/e6e3f66332c91e5a4c0c/maintainability" alt="Maintainability"></a>
|
||||
@@ -31,41 +20,71 @@ Customer engagement suite, an open-source alternative to Intercom, Zendesk, Sale
|
||||
<a href="https://artifacthub.io/packages/helm/chatwoot/chatwoot"><img src="https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/artifact-hub" alt="Artifact HUB"></a>
|
||||
</p>
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/2246121/282255783-ee8a50c9-f42d-4752-8201-2d59965a663d.png#gh-light-mode-only" width="100%" alt="Chat dashboard dark mode"/>
|
||||
<img src="https://user-images.githubusercontent.com/2246121/282255784-3d1994ec-d895-4ff5-ac68-d819987e1869.png#gh-dark-mode-only" width="100%" alt="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.
|
||||
<p>
|
||||
<a href="https://heroku.com/deploy?template=https://github.com/chatwoot/chatwoot/tree/master" alt="Deploy to Heroku">
|
||||
<img width="150" alt="Deploy" src="https://www.herokucdn.com/deploy/button.svg"/>
|
||||
</a>
|
||||
<a href="https://marketplace.digitalocean.com/apps/chatwoot?refcode=f2238426a2a8" alt="Deploy to DigitalOcean">
|
||||
<img width="200" alt="Deploy to DO" src="https://www.deploytodo.com/do-btn-blue.svg"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
<img src="./.github/screenshots/dashboard.png#gh-light-mode-only" width="100%" alt="Chat dashboard dark mode"/>
|
||||
<img src="./.github/screenshots/dashboard-dark.png#gh-dark-mode-only" width="100%" alt="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):
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
<template v-if="currentAccountId">
|
||||
<PendingEmailVerificationBanner v-if="hideOnOnboardingView" />
|
||||
<PaymentPendingBanner v-if="hideOnOnboardingView" />
|
||||
<UpgradeBanner />
|
||||
</template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="fade" mode="out-in">
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
@import 'rtl';
|
||||
|
||||
@import 'widgets/base';
|
||||
@import 'widgets/buttons';
|
||||
@import 'widgets/conversation-view';
|
||||
@import 'widgets/tabs';
|
||||
@import 'widgets/woot-tables';
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -55,13 +55,13 @@ defineExpose({ dialogRef, contactsFormRef, onSuccess });
|
||||
@click="closeDialog"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
:label="
|
||||
t('CONTACTS_LAYOUT.HEADER.ACTIONS.CONTACT_CREATION.SAVE_CONTACT')
|
||||
"
|
||||
color="blue"
|
||||
:disabled="contactsFormRef?.isFormInvalid"
|
||||
:is-loading="isCreatingContact"
|
||||
@click="handleDialogConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<!-- DEPRECIATED -->
|
||||
<!-- TODO: Replace this banner component with NextBanner "app/javascript/dashboard/components-next/banner/Banner.vue" -->
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ const STYLE_CONFIG = {
|
||||
const variantClasses = computed(() => {
|
||||
const variantMap = {
|
||||
ghost: `${STYLE_CONFIG.colors[computedColor.value].ghost}`,
|
||||
link: `${STYLE_CONFIG.colors[computedColor.value].link} p-0 font-medium underline-offset-4`,
|
||||
link: `${STYLE_CONFIG.colors[computedColor.value].link} p-0 font-medium underline-offset-2`,
|
||||
outline: STYLE_CONFIG.colors[computedColor.value].outline,
|
||||
faded: STYLE_CONFIG.colors[computedColor.value].faded,
|
||||
solid: STYLE_CONFIG.colors[computedColor.value].solid,
|
||||
|
||||
@@ -103,7 +103,7 @@ export default {
|
||||
{{ $t('FILTER.CUSTOM_VIEWS.ADD.TITLE') }}
|
||||
</h3>
|
||||
<form class="w-full grid gap-6" @submit.prevent="saveCustomViews">
|
||||
<div>
|
||||
<label :class="{ error: v$.name.$error }">
|
||||
<input
|
||||
v-model="name"
|
||||
class="py-1.5 px-3 text-n-slate-12 bg-n-alpha-1 text-sm rounded-lg reset-base w-full"
|
||||
@@ -116,14 +116,14 @@ export default {
|
||||
>
|
||||
{{ $t('FILTER.CUSTOM_VIEWS.ADD.ERROR_MESSAGE') }}
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<div class="flex flex-row justify-end w-full gap-2">
|
||||
<NextButton sm solid blue :disabled="isButtonDisabled">
|
||||
{{ $t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON') }}
|
||||
</NextButton>
|
||||
<NextButton faded slate sm @click.prevent="onClose">
|
||||
{{ $t('FILTER.CUSTOM_VIEWS.ADD.CANCEL_BUTTON') }}
|
||||
</NextButton>
|
||||
<NextButton solid blue sm :disabled="isButtonDisabled">
|
||||
{{ $t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON') }}
|
||||
</NextButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@ const onToggle = () => {
|
||||
<template>
|
||||
<div class="text-sm">
|
||||
<button
|
||||
class="flex items-center select-none w-full rounded-lg bg-n-slate-2 border border-n-weak m-0 cursor-grab justify-between py-2 px-4 drag-handle"
|
||||
class="flex items-center select-none w-full rounded-lg bg-n-slate-2 outline outline-1 outline-n-weak m-0 cursor-grab justify-between py-2 px-4 drag-handle"
|
||||
:class="{ 'rounded-bl-none rounded-br-none': isOpen }"
|
||||
@click.stop="onToggle"
|
||||
>
|
||||
@@ -55,7 +55,7 @@ const onToggle = () => {
|
||||
</button>
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="bg-n-background border border-n-weak dark:border-n-slate-2 border-t-0 rounded-br-lg rounded-bl-lg"
|
||||
class="bg-n-background outline outline-1 outline-n-weak -mt-[-1px] border-t-0 rounded-br-lg rounded-bl-lg"
|
||||
:class="compact ? 'p-0' : 'px-2 py-4'"
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -61,6 +61,7 @@ import {
|
||||
getUserPermissions,
|
||||
filterItemsByPermission,
|
||||
} from 'dashboard/helper/permissionsHelper.js';
|
||||
import { matchesFilters } from '../store/modules/conversations/helpers/filterHelpers';
|
||||
import { CONVERSATION_EVENTS } from '../helper/AnalyticsHelper/events';
|
||||
import { ASSIGNEE_TYPE_TAB_PERMISSIONS } from 'dashboard/constants/permissions.js';
|
||||
|
||||
@@ -105,7 +106,7 @@ const advancedFilterTypes = ref(
|
||||
);
|
||||
|
||||
const currentUser = useMapGetter('getCurrentUser');
|
||||
const chatLists = useMapGetter('getAllConversations');
|
||||
const chatLists = useMapGetter('getFilteredConversations');
|
||||
const mineChatsList = useMapGetter('getMineChats');
|
||||
const allChatList = useMapGetter('getAllStatusChats');
|
||||
const unAssignedChatsList = useMapGetter('getUnAssignedChats');
|
||||
@@ -324,6 +325,14 @@ const conversationList = computed(() => {
|
||||
} else {
|
||||
localConversationList = [...chatLists.value];
|
||||
}
|
||||
|
||||
if (activeFolder.value) {
|
||||
const { payload } = activeFolder.value.query;
|
||||
localConversationList = localConversationList.filter(conversation => {
|
||||
return matchesFilters(conversation, payload);
|
||||
});
|
||||
}
|
||||
|
||||
return localConversationList;
|
||||
});
|
||||
|
||||
@@ -460,6 +469,12 @@ function setParamsForEditFolderModal() {
|
||||
campaigns: campaigns.value,
|
||||
languages: languages,
|
||||
countries: countries,
|
||||
priority: [
|
||||
{ id: 'low', name: t('CONVERSATION.PRIORITY.OPTIONS.LOW') },
|
||||
{ id: 'medium', name: t('CONVERSATION.PRIORITY.OPTIONS.MEDIUM') },
|
||||
{ id: 'high', name: t('CONVERSATION.PRIORITY.OPTIONS.HIGH') },
|
||||
{ id: 'urgent', name: t('CONVERSATION.PRIORITY.OPTIONS.URGENT') },
|
||||
],
|
||||
filterTypes: advancedFilterTypes.value,
|
||||
allCustomAttributes: conversationCustomAttributes.value,
|
||||
};
|
||||
|
||||
@@ -9,12 +9,15 @@ import { getRegexp } from 'shared/helpers/Validators';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const DATE_FORMAT = 'yyyy-MM-dd';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MultiselectDropdown,
|
||||
HelperTextPopup,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
label: { type: String, required: true },
|
||||
@@ -219,14 +222,13 @@ export default {
|
||||
class="mt-0.5"
|
||||
/>
|
||||
</span>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="showActions && value"
|
||||
v-tooltip.left="$t('CUSTOM_ATTRIBUTES.ACTIONS.DELETE')"
|
||||
variant="link"
|
||||
size="medium"
|
||||
color-scheme="secondary"
|
||||
icon="delete"
|
||||
class-names="flex justify-end !w-fit"
|
||||
slate
|
||||
sm
|
||||
link
|
||||
icon="i-lucide-trash-2"
|
||||
@click="onDelete"
|
||||
/>
|
||||
</div>
|
||||
@@ -246,10 +248,10 @@ export default {
|
||||
@keyup.enter="onUpdate"
|
||||
/>
|
||||
<div>
|
||||
<woot-button
|
||||
size="small"
|
||||
icon="checkmark"
|
||||
class="ltr:rounded-l-none rtl:rounded-r-none"
|
||||
<NextButton
|
||||
sm
|
||||
icon="i-lucide-check"
|
||||
class="ltr:rounded-l-none rtl:rounded-r-none h-[34px]"
|
||||
@click="onUpdate"
|
||||
/>
|
||||
</div>
|
||||
@@ -281,25 +283,27 @@ export default {
|
||||
>
|
||||
{{ displayValue || '---' }}
|
||||
</p>
|
||||
<div class="flex max-w-[2rem] gap-1 ml-1 rtl:mr-1 rtl:ml-0">
|
||||
<woot-button
|
||||
<div
|
||||
class="flex items-center max-w-[2rem] gap-1 ml-1 rtl:mr-1 rtl:ml-0"
|
||||
>
|
||||
<NextButton
|
||||
v-if="showActions && value"
|
||||
v-tooltip="$t('CUSTOM_ATTRIBUTES.ACTIONS.COPY')"
|
||||
variant="link"
|
||||
size="small"
|
||||
color-scheme="secondary"
|
||||
icon="clipboard"
|
||||
class-names="hidden group-hover:flex !w-6 flex-shrink-0"
|
||||
xs
|
||||
slate
|
||||
ghost
|
||||
icon="i-lucide-clipboard"
|
||||
class="hidden group-hover:flex flex-shrink-0"
|
||||
@click="onCopy"
|
||||
/>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="showActions"
|
||||
v-tooltip.right="$t('CUSTOM_ATTRIBUTES.ACTIONS.EDIT')"
|
||||
variant="link"
|
||||
size="small"
|
||||
color-scheme="secondary"
|
||||
icon="edit"
|
||||
class-names="hidden group-hover:flex !w-6 flex-shrink-0"
|
||||
xs
|
||||
slate
|
||||
ghost
|
||||
icon="i-lucide-pen"
|
||||
class="hidden group-hover:flex flex-shrink-0"
|
||||
@click="onEdit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import DatePicker from 'vue-datepicker-next';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DatePicker,
|
||||
NextButton,
|
||||
},
|
||||
emits: ['close', 'chooseTime'],
|
||||
|
||||
@@ -52,18 +54,23 @@ export default {
|
||||
v-model:value="snoozeTime"
|
||||
type="datetime"
|
||||
inline
|
||||
input-class="mx-input reset-base"
|
||||
input-class="mx-input "
|
||||
:lang="lang"
|
||||
:disabled-date="disabledDate"
|
||||
:disabled-time="disabledTime"
|
||||
/>
|
||||
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
|
||||
<woot-button variant="clear" @click.prevent="onClose">
|
||||
{{ $t('CONVERSATION.CUSTOM_SNOOZE.CANCEL') }}
|
||||
</woot-button>
|
||||
<woot-button>
|
||||
{{ $t('CONVERSATION.CUSTOM_SNOOZE.APPLY') }}
|
||||
</woot-button>
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('CONVERSATION.CUSTOM_SNOOZE.CANCEL')"
|
||||
@click.prevent="onClose"
|
||||
/>
|
||||
<NextButton
|
||||
type="submit"
|
||||
:label="$t('CONVERSATION.CUSTOM_SNOOZE.APPLY')"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<script>
|
||||
import 'highlight.js/styles/default.css';
|
||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
masked: true,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async onCopy(e) {
|
||||
e.preventDefault();
|
||||
await copyTextToClipboard(this.value);
|
||||
useAlert(this.$t('COMPONENTS.CODE.COPY_SUCCESSFUL'));
|
||||
},
|
||||
toggleMasked() {
|
||||
this.masked = !this.masked;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text--container">
|
||||
<woot-button size="small" class="button--text" @click="onCopy">
|
||||
{{ $t('COMPONENTS.CODE.BUTTON_TEXT') }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
size="small"
|
||||
class="button--visibility"
|
||||
color-scheme="secondary"
|
||||
:icon="masked ? 'eye-show' : 'eye-hide'"
|
||||
@click.prevent="toggleMasked"
|
||||
/>
|
||||
<highlightjs v-if="value" :code="masked ? '•'.repeat(10) : value" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.text--container {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
|
||||
.button--text,
|
||||
.button--visibility {
|
||||
margin-top: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.button--visibility {
|
||||
right: 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@
|
||||
// [TODO] Use Teleport to move the modal to the end of the body
|
||||
import { ref, computed, defineEmits, onMounted } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const { modalType, closeOnBackdropClick, onClose } = defineProps({
|
||||
closeOnBackdropClick: { type: Boolean, default: true },
|
||||
@@ -85,11 +86,11 @@ onMounted(() => {
|
||||
@mouse.stop
|
||||
@mousedown="event => event.stopPropagation()"
|
||||
>
|
||||
<woot-button
|
||||
<Button
|
||||
v-if="showCloseButton"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
variant="clear"
|
||||
ghost
|
||||
slate
|
||||
icon="i-lucide-x"
|
||||
class="absolute z-10 ltr:right-2 rtl:left-2 top-2"
|
||||
@click="close"
|
||||
/>
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
} from 'dashboard/helper/routeHelpers';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
@@ -107,34 +109,31 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template>
|
||||
<transition name="network-notification-fade" tag="div">
|
||||
<div v-show="showNotification" class="fixed z-50 top-4 left-2 group">
|
||||
<div v-show="showNotification" class="fixed z-50 top-2 left-2 group">
|
||||
<div
|
||||
class="relative flex items-center justify-between w-full px-2 py-1 bg-yellow-200 rounded-lg shadow-lg dark:bg-yellow-700"
|
||||
class="relative flex items-center justify-between w-full px-2 py-1 bg-n-amber-4 dark:bg-n-amber-8 rounded-lg shadow-lg"
|
||||
>
|
||||
<fluent-icon
|
||||
:icon="iconName"
|
||||
class="text-yellow-700/50 dark:text-yellow-50"
|
||||
size="18"
|
||||
/>
|
||||
<span
|
||||
class="px-2 text-xs font-medium tracking-wide text-yellow-700/70 dark:text-yellow-50"
|
||||
>
|
||||
<fluent-icon :icon="iconName" class="text-n-amber-12" size="18" />
|
||||
<span class="px-2 text-xs font-medium tracking-wide text-n-amber-12">
|
||||
{{ bannerText }}
|
||||
</span>
|
||||
<woot-button
|
||||
<Button
|
||||
v-if="canRefresh"
|
||||
ghost
|
||||
sm
|
||||
amber
|
||||
icon="i-lucide-refresh-ccw"
|
||||
:title="$t('NETWORK.BUTTON.REFRESH')"
|
||||
variant="clear"
|
||||
size="small"
|
||||
color-scheme="warning"
|
||||
icon="arrow-clockwise"
|
||||
class="!text-n-amber-12 dark:!text-n-amber-9"
|
||||
@click="refreshPage"
|
||||
/>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
size="small"
|
||||
color-scheme="warning"
|
||||
icon="dismiss"
|
||||
|
||||
<Button
|
||||
ghost
|
||||
sm
|
||||
amber
|
||||
icon="i-lucide-x"
|
||||
class="!text-n-amber-12 dark:!text-n-amber-9"
|
||||
@click="closeNotification"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -3,12 +3,16 @@ import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
default: 'small',
|
||||
default: 'sm',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
@@ -33,13 +37,13 @@ export default {
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-root-v-if -->
|
||||
<template>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="!hasNextSidebar"
|
||||
ghost
|
||||
slate
|
||||
:size="size"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
class="-ml-3 text-black-900 dark:text-slate-300"
|
||||
icon="list"
|
||||
icon="i-lucide-menu"
|
||||
class="-ml-3"
|
||||
@click="onMenuItemClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
color-scheme="alert"
|
||||
:banner-message="bannerMessage"
|
||||
:action-button-label="actionButtonMessage"
|
||||
action-button-icon="mail"
|
||||
action-button-icon="i-lucide-mail"
|
||||
has-action-button
|
||||
@primary-action="resendVerificationEmail"
|
||||
/>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<script>
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
props: {
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonIconClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'button',
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (import.meta.env.DEV) {
|
||||
// eslint-disable-next-line
|
||||
console.warn(
|
||||
'[DEPRECATED] This component has been deprecated and will be removed soon. Please use v3/components/Form/Button.vue instead'
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button :type="type" class="button nice" :class="variant">
|
||||
<fluent-icon
|
||||
v-if="!isLoading && icon"
|
||||
class="icon"
|
||||
:class="buttonIconClass"
|
||||
:icon="icon"
|
||||
/>
|
||||
<Spinner v-if="isLoading" />
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
@@ -1,66 +0,0 @@
|
||||
<script>
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Spinner,
|
||||
},
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
buttonClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
iconClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
spinnerClass: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'submit',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
computedClass() {
|
||||
return `button nice gap-2 ${this.buttonClass || ' '}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:type="type"
|
||||
data-testid="submit_button"
|
||||
:disabled="disabled"
|
||||
:class="computedClass"
|
||||
>
|
||||
<fluent-icon v-if="!!iconClass" :icon="iconClass" class="icon" />
|
||||
<span>{{ buttonText }}</span>
|
||||
<Spinner v-if="loading" class="ml-2" :color-scheme="spinnerClass" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
button:disabled {
|
||||
@apply bg-woot-100 dark:bg-woot-500/25 dark:text-slate-500 opacity-100;
|
||||
&:hover {
|
||||
@apply bg-woot-100 dark:bg-woot-500/25;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -134,7 +134,7 @@ useEmitter(CMD_RESOLVE_CONVERSATION, onCmdResolveConversation);
|
||||
<template>
|
||||
<div class="relative flex items-center justify-end resolve-actions">
|
||||
<div
|
||||
class="rounded-lg shadow button-group outline-1 outline"
|
||||
class="rounded-lg shadow outline-1 outline"
|
||||
:class="!showOpenButton ? 'outline-n-container' : 'outline-transparent'"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// [NOTE][DEPRECATED] This method is to be deprecated, please do not add new components to this file.
|
||||
/* eslint no-plusplus: 0 */
|
||||
import AvatarUploader from './widgets/forms/AvatarUploader.vue';
|
||||
import Button from './ui/WootButton.vue';
|
||||
import Code from './Code.vue';
|
||||
import ColorPicker from './widgets/ColorPicker.vue';
|
||||
import ConfirmDeleteModal from './widgets/modal/ConfirmDeleteModal.vue';
|
||||
@@ -18,7 +17,6 @@ import ModalHeader from './ModalHeader.vue';
|
||||
import Modal from './Modal.vue';
|
||||
import SidemenuIcon from './SidemenuIcon.vue';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import SubmitButton from './buttons/FormSubmitButton.vue';
|
||||
import Tabs from './ui/Tabs/Tabs.vue';
|
||||
import TabsItem from './ui/Tabs/TabsItem.vue';
|
||||
import Thumbnail from './widgets/Thumbnail.vue';
|
||||
@@ -26,7 +24,6 @@ import DatePicker from './ui/DatePicker/DatePicker.vue';
|
||||
|
||||
const WootUIKit = {
|
||||
AvatarUploader,
|
||||
Button,
|
||||
Code,
|
||||
ColorPicker,
|
||||
ConfirmDeleteModal,
|
||||
@@ -43,7 +40,6 @@ const WootUIKit = {
|
||||
ModalHeader,
|
||||
SidemenuIcon,
|
||||
Spinner,
|
||||
SubmitButton,
|
||||
Tabs,
|
||||
TabsItem,
|
||||
Thumbnail,
|
||||
|
||||
@@ -7,6 +7,7 @@ import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader.vue
|
||||
import WootDropdownDivider from 'shared/components/ui/dropdown/DropdownDivider.vue';
|
||||
import AvailabilityStatusBadge from '../widgets/conversation/AvailabilityStatusBadge.vue';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const { AVAILABILITY_STATUS_KEYS } = wootConstants;
|
||||
|
||||
@@ -17,6 +18,7 @@ export default {
|
||||
WootDropdownMenu,
|
||||
WootDropdownItem,
|
||||
AvailabilityStatusBadge,
|
||||
NextButton,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -101,19 +103,21 @@ export default {
|
||||
:key="status.value"
|
||||
class="flex items-baseline"
|
||||
>
|
||||
<woot-button
|
||||
size="small"
|
||||
:color-scheme="status.disabled ? '' : 'secondary'"
|
||||
:variant="status.disabled ? 'smooth' : 'clear'"
|
||||
class="status-change--dropdown-button"
|
||||
<NextButton
|
||||
sm
|
||||
:color="status.disabled ? 'blue' : 'slate'"
|
||||
:variant="status.disabled ? 'faded' : 'ghost'"
|
||||
class="status-change--dropdown-button !w-full !justify-start"
|
||||
@click="changeAvailabilityStatus(status.value)"
|
||||
>
|
||||
<AvailabilityStatusBadge :status="status.value" />
|
||||
{{ status.label }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ status.label }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownDivider />
|
||||
<WootDropdownItem class="flex items-center justify-between p-2 m-0">
|
||||
<WootDropdownItem class="flex items-center justify-between px-3 py-2 m-0">
|
||||
<div class="flex items-center">
|
||||
<fluent-icon
|
||||
v-tooltip.right-start="$t('SIDEBAR.SET_AUTO_OFFLINE.INFO_TEXT')"
|
||||
@@ -123,7 +127,7 @@ export default {
|
||||
/>
|
||||
|
||||
<span
|
||||
class="mx-1 my-0 text-xs font-medium text-slate-600 dark:text-slate-100"
|
||||
class="mx-2 my-0 text-xs font-medium text-slate-600 dark:text-slate-100"
|
||||
>
|
||||
{{ $t('SIDEBAR.SET_AUTO_OFFLINE.TEXT') }}
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
emits: ['toggleAccounts'],
|
||||
data() {
|
||||
return { showSwitchButton: false };
|
||||
@@ -46,14 +50,13 @@ export default {
|
||||
class="absolute top-0 right-0 flex items-center justify-end w-full h-full rounded-md ltr:overlay-shadow ltr:dark:overlay-shadow-dark rtl:rtl-overlay-shadow rtl:dark:rtl-overlay-shadow-dark"
|
||||
>
|
||||
<div class="mx-2 my-0">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
size="tiny"
|
||||
icon="arrow-swap"
|
||||
<NextButton
|
||||
ghost
|
||||
xs
|
||||
icon="i-lucide-arrow-right-left"
|
||||
:label="$t('SIDEBAR.SWITCH')"
|
||||
@click="$emit('toggleAccounts')"
|
||||
>
|
||||
{{ $t('SIDEBAR.SWITCH') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@@ -3,8 +3,12 @@ import { required, minLength } from '@vuelidate/validators';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
@@ -86,19 +90,24 @@ export default {
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div class="w-full">
|
||||
<woot-submit-button
|
||||
:disabled="
|
||||
v$.accountName.$invalid ||
|
||||
v$.accountName.$invalid ||
|
||||
uiFlags.isCreating
|
||||
"
|
||||
:button-text="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
||||
:loading="uiFlags.isCreating"
|
||||
button-class="large expanded"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full flex justify-end gap-2 items-center">
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('CREATE_ACCOUNT.FORM.CANCEL')"
|
||||
@click.prevent="() => $emit('closeAccountCreateModal')"
|
||||
/>
|
||||
<NextButton
|
||||
type="submit"
|
||||
:label="$t('CREATE_ACCOUNT.FORM.SUBMIT')"
|
||||
:is-loading="uiFlags.isCreating"
|
||||
:disabled="
|
||||
v$.accountName.$invalid ||
|
||||
v$.accountName.$invalid ||
|
||||
uiFlags.isCreating
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import Thumbnail from '../../widgets/Thumbnail.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Thumbnail,
|
||||
NextButton,
|
||||
},
|
||||
emits: ['toggleMenu'],
|
||||
computed: {
|
||||
@@ -25,10 +27,10 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-tooltip.right="$t(`SIDEBAR.PROFILE_SETTINGS`)"
|
||||
variant="link"
|
||||
class="flex items-center rounded-full"
|
||||
link
|
||||
class="rounded-full"
|
||||
@click="handleClick"
|
||||
>
|
||||
<Thumbnail
|
||||
@@ -37,6 +39,7 @@ export default {
|
||||
:status="statusOfAgent"
|
||||
should-show-status-always
|
||||
size="32px"
|
||||
class="flex-shrink-0"
|
||||
/>
|
||||
</woot-button>
|
||||
</NextButton>
|
||||
</template>
|
||||
|
||||
@@ -5,12 +5,14 @@ import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
import AvailabilityStatus from 'dashboard/components/layout/AvailabilityStatus.vue';
|
||||
import { FEATURE_FLAGS } from '../../../featureFlags';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
WootDropdownMenu,
|
||||
WootDropdownItem,
|
||||
AvailabilityStatus,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
@@ -82,37 +84,46 @@ export default {
|
||||
<AvailabilityStatus />
|
||||
<WootDropdownMenu>
|
||||
<WootDropdownItem v-if="showChangeAccountOption">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="arrow-swap"
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-arrow-right-left"
|
||||
class="!w-full !justify-start"
|
||||
@click="$emit('toggleAccounts')"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.CHANGE_ACCOUNTS') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem v-if="showChatSupport">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="chat-help"
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-message-circle-question"
|
||||
class="!w-full !justify-start"
|
||||
@click="$emit('showSupportChatWindow')"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.CONTACT_SUPPORT') }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.CONTACT_SUPPORT') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="keyboard"
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-keyboard"
|
||||
class="!w-full !justify-start"
|
||||
@click="handleKeyboardHelpClick"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.KEYBOARD_SHORTCUTS') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem>
|
||||
<router-link
|
||||
@@ -122,56 +133,70 @@ export default {
|
||||
>
|
||||
<a
|
||||
:href="href"
|
||||
class="h-8 bg-white button small clear secondary dark:bg-slate-800"
|
||||
:class="{ 'is-active': isActive }"
|
||||
@click="e => handleProfileSettingClick(e, navigate)"
|
||||
>
|
||||
<fluent-icon icon="person" size="14" class="icon icon--font" />
|
||||
<span class="button__content">
|
||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||
</span>
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-circle-user"
|
||||
class="!w-full !justify-start"
|
||||
>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.PROFILE_SETTINGS') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</a>
|
||||
</router-link>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="appearance"
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-sun-moon"
|
||||
class="!w-full !justify-start"
|
||||
@click="openAppearanceOptions"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.APPEARANCE') }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.APPEARANCE') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem v-if="currentUser.type === 'SuperAdmin'">
|
||||
<a
|
||||
href="/super_admin"
|
||||
class="h-8 bg-white button small clear secondary dark:bg-slate-800"
|
||||
target="_blank"
|
||||
rel="noopener nofollow noreferrer"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="content-settings"
|
||||
size="14"
|
||||
class="icon icon--font"
|
||||
/>
|
||||
<span class="button__content">
|
||||
{{ $t('SIDEBAR_ITEMS.SUPER_ADMIN_CONSOLE') }}
|
||||
</span>
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-layout-dashboard"
|
||||
class="!w-full !justify-start"
|
||||
>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.SUPER_ADMIN_CONSOLE') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</a>
|
||||
</WootDropdownItem>
|
||||
<WootDropdownItem>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
icon="power"
|
||||
<NextButton
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-circle-power"
|
||||
class="!w-full !justify-start"
|
||||
@click="logout"
|
||||
>
|
||||
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
||||
</woot-button>
|
||||
<span class="min-w-0 truncate font-medium text-xs">
|
||||
{{ $t('SIDEBAR_ITEMS.LOGOUT') }}
|
||||
</span>
|
||||
</NextButton>
|
||||
</WootDropdownItem>
|
||||
</WootDropdownMenu>
|
||||
</div>
|
||||
|
||||
@@ -13,9 +13,10 @@ import {
|
||||
isOnUnattendedView,
|
||||
} from '../../../store/modules/conversations/helpers/actionHelpers';
|
||||
import Policy from '../../policy.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: { SecondaryChildNavItem, Policy },
|
||||
components: { SecondaryChildNavItem, Policy, NextButton },
|
||||
props: {
|
||||
menuItem: {
|
||||
type: Object,
|
||||
@@ -205,14 +206,7 @@ export default {
|
||||
{{ $t(`SIDEBAR.${menuItem.label}`) }}
|
||||
</span>
|
||||
<div v-if="menuItem.showNewButton" class="flex items-center">
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="add"
|
||||
class="p-0 ml-2"
|
||||
@click="onClickOpen"
|
||||
/>
|
||||
<NextButton ghost xs slate icon="i-lucide-plus" @click="onClickOpen" />
|
||||
</div>
|
||||
</div>
|
||||
<router-link
|
||||
@@ -272,16 +266,15 @@ export default {
|
||||
>
|
||||
<li class="pl-1">
|
||||
<a :href="href">
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="add"
|
||||
<NextButton
|
||||
ghost
|
||||
xs
|
||||
slate
|
||||
icon="i-lucide-plus"
|
||||
:label="$t(`SIDEBAR.${menuItem.newLinkTag}`)"
|
||||
:data-testid="menuItem.dataTestid"
|
||||
@click="e => newLinkClick(e, navigate)"
|
||||
>
|
||||
{{ $t(`SIDEBAR.${menuItem.newLinkTag}`) }}
|
||||
</woot-button>
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</router-link>
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ describe('AccountSelector', () => {
|
||||
'fluent-icon': FluentIcon,
|
||||
},
|
||||
stubs: {
|
||||
WootButton: { template: '<button />' },
|
||||
// override global stub
|
||||
WootModalHeader: false,
|
||||
},
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
|
||||
import { createStore } from 'vuex';
|
||||
import AgentDetails from '../AgentDetails.vue';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import WootButton from 'dashboard/components/ui/WootButton.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
describe('AgentDetails', () => {
|
||||
const currentUser = {
|
||||
@@ -40,12 +40,12 @@ describe('AgentDetails', () => {
|
||||
plugins: [store],
|
||||
components: {
|
||||
Thumbnail,
|
||||
WootButton,
|
||||
NextButton,
|
||||
},
|
||||
directives: {
|
||||
tooltip: mockTooltipDirective, // Mocking the tooltip directive
|
||||
},
|
||||
stubs: { WootButton: { template: '<button><slot /></button>' } },
|
||||
stubs: { NextButton: { template: '<button><slot /></button>' } },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { createStore } from 'vuex';
|
||||
import AvailabilityStatus from '../AvailabilityStatus.vue';
|
||||
import WootButton from 'dashboard/components/ui/WootButton.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
import WootDropdownHeader from 'shared/components/ui/dropdown/DropdownHeader.vue';
|
||||
@@ -40,7 +40,7 @@ describe('AvailabilityStatus', () => {
|
||||
global: {
|
||||
plugins: [store],
|
||||
components: {
|
||||
WootButton,
|
||||
NextButton,
|
||||
WootDropdownItem,
|
||||
WootDropdownMenu,
|
||||
WootDropdownHeader,
|
||||
|
||||
@@ -22,7 +22,7 @@ const store = createStore({
|
||||
describe('SidemenuIcon', () => {
|
||||
test('matches snapshot', () => {
|
||||
const wrapper = shallowMount(SidemenuIcon, {
|
||||
stubs: { WootButton: { template: '<button><slot /></button>' } },
|
||||
stubs: { NextButton: { template: '<button><slot /></button>' } },
|
||||
global: { plugins: [store] },
|
||||
});
|
||||
expect(wrapper.vm).toBeTruthy();
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
exports[`SidemenuIcon > matches snapshot 1`] = `
|
||||
<button
|
||||
class="-ml-3 text-black-900 dark:text-slate-300"
|
||||
color-scheme="secondary"
|
||||
icon="list"
|
||||
size="small"
|
||||
variant="clear"
|
||||
class="-ml-3"
|
||||
ghost=""
|
||||
icon="i-lucide-menu"
|
||||
size="sm"
|
||||
slate=""
|
||||
>
|
||||
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
popupMessage: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
routeText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hasCloseButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
closeButtonText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
emits: ['open', 'close'],
|
||||
methods: {
|
||||
onClickOpenPath() {
|
||||
this.$emit('open');
|
||||
},
|
||||
onClickClose(e) {
|
||||
this.$emit('close', e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="announcement-popup">
|
||||
<span v-if="popupMessage" class="popup-content">
|
||||
{{ popupMessage }}
|
||||
<span v-if="routeText" class="route-url" @click="onClickOpenPath">
|
||||
{{ routeText }}
|
||||
</span>
|
||||
</span>
|
||||
<div v-if="hasCloseButton" class="popup-close">
|
||||
<woot-button
|
||||
v-if="hasCloseButton"
|
||||
color-scheme="primary"
|
||||
variant="link"
|
||||
size="small"
|
||||
@click="onClickClose"
|
||||
>
|
||||
{{ closeButtonText }}
|
||||
</woot-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.announcement-popup {
|
||||
max-width: 15rem;
|
||||
min-width: 10rem;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
height: fit-content;
|
||||
background: var(--white);
|
||||
padding: 0 var(--space-normal);
|
||||
z-index: var(--z-index-much-higher);
|
||||
box-shadow: var(--b-200) 4px 4px 16px 4px;
|
||||
border-radius: var(--border-radius-normal);
|
||||
|
||||
.popup-content {
|
||||
font-size: var(--font-size-mini);
|
||||
color: var(--s-700);
|
||||
padding: var(--space-one) 0;
|
||||
.route-url {
|
||||
font-size: var(--font-size-mini);
|
||||
color: var(--s-600);
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--space-one) 0;
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,10 @@
|
||||
<script>
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
bannerMessage: {
|
||||
type: String,
|
||||
@@ -19,7 +24,7 @@ export default {
|
||||
},
|
||||
actionButtonVariant: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: 'faded',
|
||||
},
|
||||
actionButtonLabel: {
|
||||
type: String,
|
||||
@@ -27,7 +32,7 @@ export default {
|
||||
},
|
||||
actionButtonIcon: {
|
||||
type: String,
|
||||
default: 'arrow-right',
|
||||
default: 'i-lucide-arrow-right',
|
||||
},
|
||||
colorScheme: {
|
||||
type: String,
|
||||
@@ -48,6 +53,18 @@ export default {
|
||||
}
|
||||
return classList;
|
||||
},
|
||||
// TODO - Remove this method when we standardize
|
||||
// the button color and variant names
|
||||
getButtonColor() {
|
||||
const colorMap = {
|
||||
primary: 'blue',
|
||||
secondary: 'blue',
|
||||
alert: 'ruby',
|
||||
warning: 'amber',
|
||||
};
|
||||
|
||||
return colorMap[this.colorScheme] || 'blue';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
@@ -77,27 +94,23 @@ export default {
|
||||
</a>
|
||||
</span>
|
||||
<div class="actions">
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="hasActionButton"
|
||||
size="tiny"
|
||||
xs
|
||||
:icon="actionButtonIcon"
|
||||
:variant="actionButtonVariant"
|
||||
color-scheme="primary"
|
||||
class-names="banner-action__button"
|
||||
:color="getButtonColor"
|
||||
:label="actionButtonLabel"
|
||||
@click="onClick"
|
||||
>
|
||||
{{ actionButtonLabel }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
/>
|
||||
<NextButton
|
||||
v-if="hasCloseButton"
|
||||
size="tiny"
|
||||
:color-scheme="colorScheme"
|
||||
icon="dismiss-circle"
|
||||
class-names="banner-action__button"
|
||||
xs
|
||||
icon="i-lucide-circle-x"
|
||||
:color="getButtonColor"
|
||||
:label="$t('GENERAL_SETTINGS.DISMISS')"
|
||||
@click="onClickClose"
|
||||
>
|
||||
{{ $t('GENERAL_SETTINGS.DISMISS') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -106,13 +119,6 @@ export default {
|
||||
.banner {
|
||||
&.primary {
|
||||
@apply bg-woot-500 dark:bg-woot-500;
|
||||
.banner-action__button {
|
||||
@apply bg-woot-600 dark:bg-woot-600 border-none text-white;
|
||||
|
||||
&:hover {
|
||||
@apply bg-woot-700 dark:bg-woot-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
@@ -124,13 +130,6 @@ export default {
|
||||
|
||||
&.alert {
|
||||
@apply bg-n-ruby-3 text-n-ruby-12;
|
||||
.banner-action__button {
|
||||
@apply border-none text-n-ruby-12 bg-n-ruby-5;
|
||||
|
||||
&:hover {
|
||||
@apply bg-n-ruby-4;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-n-ruby-12;
|
||||
@@ -146,21 +145,12 @@ export default {
|
||||
|
||||
&.gray {
|
||||
@apply text-black-500 dark:text-black-500;
|
||||
.banner-action__button {
|
||||
@apply text-white dark:text-white;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@apply ml-1 underline text-white dark:text-white text-xs;
|
||||
}
|
||||
|
||||
.banner-action__button {
|
||||
::v-deep .button__content {
|
||||
@apply whitespace-nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.banner-message {
|
||||
@apply flex items-center;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<script setup>
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
buttonText: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rightIcon: {
|
||||
type: String,
|
||||
default: '',
|
||||
trailingIcon: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
leftIcon: {
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
@@ -16,32 +18,15 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="inline-flex relative items-center p-1.5 w-fit h-8 gap-1.5 rounded-lg hover:bg-slate-50 dark:hover:bg-slate-800 active:bg-slate-75 dark:active:bg-slate-800"
|
||||
<Button
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="relative"
|
||||
:icon="icon"
|
||||
:trailing-icon="trailingIcon"
|
||||
>
|
||||
<slot name="leftIcon">
|
||||
<fluent-icon
|
||||
v-if="leftIcon"
|
||||
:icon="leftIcon"
|
||||
size="18"
|
||||
class="flex-shrink-0 text-slate-900 dark:text-slate-50"
|
||||
/>
|
||||
</slot>
|
||||
<span
|
||||
v-if="buttonText"
|
||||
class="text-sm font-medium truncate text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</span>
|
||||
<slot name="rightIcon">
|
||||
<fluent-icon
|
||||
v-if="rightIcon"
|
||||
:icon="rightIcon"
|
||||
size="18"
|
||||
class="flex-shrink-0 text-slate-900 dark:text-slate-50"
|
||||
/>
|
||||
</slot>
|
||||
|
||||
<span class="min-w-0 truncate">{{ buttonText }}</span>
|
||||
<slot name="dropdown" />
|
||||
</button>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -8,9 +8,7 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-center h-10 text-sm text-slate-500 dark:text-slate-300"
|
||||
>
|
||||
<div class="flex items-center justify-center h-10 text-sm text-n-slate-11">
|
||||
{{ message }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -78,7 +78,7 @@ const shouldShowEmptyState = computed(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="absolute z-20 w-40 bg-white border shadow dark:bg-slate-800 rounded-xl border-slate-50 dark:border-slate-700/50 max-h-[400px]"
|
||||
class="absolute z-20 w-40 bg-n-solid-2 border-0 outline outline-1 outline-n-weak shadow rounded-xl max-h-[400px]"
|
||||
@click.stop
|
||||
>
|
||||
<slot name="search">
|
||||
|
||||
@@ -21,7 +21,7 @@ defineProps({
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="relative inline-flex items-center justify-start w-full p-3 border-0 rounded-none first:rounded-t-xl last:rounded-b-xl h-11 hover:bg-slate-50 dark:hover:bg-slate-700 active:bg-slate-75 dark:active:bg-slate-800"
|
||||
class="relative inline-flex items-center justify-start w-full p-3 border-0 rounded-none first:rounded-t-xl last:rounded-b-xl h-11 hover:enabled:bg-n-alpha-2"
|
||||
>
|
||||
<div class="inline-flex items-center gap-3 overflow-hidden">
|
||||
<fluent-icon
|
||||
@@ -30,16 +30,14 @@ defineProps({
|
||||
size="18"
|
||||
:style="{ color: iconColor }"
|
||||
/>
|
||||
<span
|
||||
class="text-sm font-medium truncate text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
<span class="text-sm font-medium truncate text-n-slate-12">
|
||||
{{ buttonText }}
|
||||
</span>
|
||||
<fluent-icon
|
||||
v-if="isActive"
|
||||
icon="checkmark"
|
||||
size="18"
|
||||
class="flex-shrink-0 text-slate-900 dark:text-slate-50"
|
||||
class="flex-shrink-0 text-n-slate-12"
|
||||
/>
|
||||
</div>
|
||||
<slot name="dropdown" />
|
||||
|
||||
@@ -8,9 +8,7 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-center h-10 text-sm text-slate-500 dark:text-slate-300"
|
||||
>
|
||||
<div class="flex items-center justify-center h-10 text-sm text-n-slate-11">
|
||||
{{ message }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup>
|
||||
import { defineEmits, defineModel } from 'vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
@@ -21,31 +23,29 @@ const value = defineModel({
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-between h-10 min-h-[40px] sticky top-0 bg-white z-10 dark:bg-slate-800 gap-2 px-3 border-b rounded-t-xl border-slate-50 dark:border-slate-700"
|
||||
class="flex items-center justify-between h-10 min-h-[40px] sticky top-0 bg-n-solid-2 dark:bg-n-solid-2 z-10 gap-2 px-3 border-b rounded-t-xl border-n-weak"
|
||||
>
|
||||
<div class="flex items-center w-full gap-2" @keyup.space.prevent>
|
||||
<fluent-icon
|
||||
icon="search"
|
||||
size="16"
|
||||
class="text-slate-400 dark:text-slate-400 flex-shrink-0"
|
||||
class="text-n-slate-11 flex-shrink-0"
|
||||
/>
|
||||
<input
|
||||
v-model="value"
|
||||
:placeholder="inputPlaceholder"
|
||||
type="text"
|
||||
class="w-full mb-0 text-sm bg-white dark:bg-slate-800 text-slate-800 dark:text-slate-75 reset-base"
|
||||
class="w-full mb-0 text-sm !outline-0 bg-transparent text-n-slate-12 placeholder:text-n-slate-10 reset-base"
|
||||
/>
|
||||
</div>
|
||||
<!-- Clear filter button -->
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="!modelValue && showClearFilter"
|
||||
size="small"
|
||||
variant="clear"
|
||||
color-scheme="primary"
|
||||
class="!px-1 !py-1.5"
|
||||
faded
|
||||
xs
|
||||
class="flex-shrink-0"
|
||||
:label="$t('REPORT.FILTER_ACTIONS.CLEAR_FILTER')"
|
||||
@click="emit('remove')"
|
||||
>
|
||||
{{ $t('REPORT.FILTER_ACTIONS.CLEAR_FILTER') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
<script>
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import EmojiOrIcon from 'shared/components/EmojiOrIcon.vue';
|
||||
|
||||
export default {
|
||||
name: 'WootButton',
|
||||
components: { EmojiOrIcon, Spinner },
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'submit',
|
||||
},
|
||||
variant: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
emoji: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
colorScheme: {
|
||||
type: String,
|
||||
default: 'primary',
|
||||
},
|
||||
classNames: {
|
||||
type: [String, Object],
|
||||
default: '',
|
||||
},
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isExpanded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
variantClasses() {
|
||||
if (this.variant.includes('link')) {
|
||||
return `clear ${this.variant}`;
|
||||
}
|
||||
return this.variant;
|
||||
},
|
||||
hasOnlyIcon() {
|
||||
const hasEmojiOrIcon = this.emoji || this.icon;
|
||||
return !this.$slots.default && hasEmojiOrIcon;
|
||||
},
|
||||
hasOnlyIconClasses() {
|
||||
return this.hasOnlyIcon ? 'button--only-icon' : '';
|
||||
},
|
||||
buttonClasses() {
|
||||
return [
|
||||
this.variantClasses,
|
||||
this.hasOnlyIconClasses,
|
||||
this.size,
|
||||
this.colorScheme,
|
||||
this.classNames,
|
||||
this.isDisabled ? 'disabled' : '',
|
||||
this.isExpanded ? 'expanded' : '',
|
||||
];
|
||||
},
|
||||
iconSize() {
|
||||
switch (this.size) {
|
||||
case 'tiny':
|
||||
return 12;
|
||||
case 'small':
|
||||
return 14;
|
||||
case 'medium':
|
||||
return 16;
|
||||
case 'large':
|
||||
return 18;
|
||||
|
||||
default:
|
||||
return 16;
|
||||
}
|
||||
},
|
||||
showDarkSpinner() {
|
||||
return (
|
||||
this.colorScheme === 'secondary' ||
|
||||
this.variant === 'clear' ||
|
||||
this.variant === 'link' ||
|
||||
this.variant === 'hollow'
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="button"
|
||||
:type="type"
|
||||
:class="buttonClasses"
|
||||
:disabled="isDisabled || isLoading"
|
||||
>
|
||||
<Spinner
|
||||
v-if="isLoading"
|
||||
size="small"
|
||||
:color-scheme="showDarkSpinner ? 'dark' : ''"
|
||||
/>
|
||||
<EmojiOrIcon
|
||||
v-else-if="icon || emoji"
|
||||
class="icon"
|
||||
:emoji="emoji"
|
||||
:icon="icon"
|
||||
:icon-size="iconSize"
|
||||
/>
|
||||
<span
|
||||
v-if="$slots.default"
|
||||
class="button__content"
|
||||
:class="{ 'text-left rtl:text-right': size !== 'expanded' }"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
@@ -2,10 +2,12 @@
|
||||
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import AILoader from './AILoader.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AILoader,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
aiOption: {
|
||||
@@ -84,16 +86,22 @@ export default {
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
|
||||
<woot-button variant="clear" @click.prevent="onClose">
|
||||
{{
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="
|
||||
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.CANCEL')
|
||||
}}
|
||||
</woot-button>
|
||||
<woot-button :disabled="!generatedContent">
|
||||
{{
|
||||
"
|
||||
@click.prevent="onClose"
|
||||
/>
|
||||
<NextButton
|
||||
type="submit"
|
||||
:disabled="!generatedContent"
|
||||
:label="
|
||||
$t('INTEGRATION_SETTINGS.OPEN_AI.ASSISTANCE_MODAL.BUTTONS.APPLY')
|
||||
}}
|
||||
</woot-button>
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,12 @@ import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
emits: ['close'],
|
||||
|
||||
setup() {
|
||||
@@ -94,16 +99,30 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between w-full gap-2 px-0 py-2">
|
||||
<woot-button variant="link" @click.prevent="openOpenAIDoc">
|
||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.NEED_HELP') }}
|
||||
</woot-button>
|
||||
<NextButton
|
||||
ghost
|
||||
type="button"
|
||||
class="!px-3"
|
||||
:label="
|
||||
$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.NEED_HELP')
|
||||
"
|
||||
@click.prevent="openOpenAIDoc"
|
||||
/>
|
||||
<div class="flex items-center gap-1">
|
||||
<woot-button variant="clear" @click.prevent="onDismiss">
|
||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.DISMISS') }}
|
||||
</woot-button>
|
||||
<woot-button :is-disabled="v$.value.$invalid">
|
||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.FINISH') }}
|
||||
</woot-button>
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="
|
||||
$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.DISMISS')
|
||||
"
|
||||
@click.prevent="onDismiss"
|
||||
/>
|
||||
<NextButton
|
||||
type="submit"
|
||||
:disabled="v$.value.$invalid"
|
||||
:label="$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.FINISH')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { computed } from 'vue';
|
||||
import { formatBytes } from 'shared/helpers/FileHelper';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
attachments: {
|
||||
type: Array,
|
||||
@@ -73,9 +75,11 @@ const fileName = file => {
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-center">
|
||||
<woot-button
|
||||
class="!w-6 !h-6 text-sm rounded-md hover:bg-slate-50 dark:hover:bg-slate-800 clear secondary"
|
||||
icon="dismiss"
|
||||
<Button
|
||||
ghost
|
||||
slate
|
||||
xs
|
||||
icon="i-lucide-x"
|
||||
@click="onRemoveAttachment(index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
import AutomationActionTeamMessageInput from './AutomationActionTeamMessageInput.vue';
|
||||
import AutomationActionFileInput from './AutomationFileInput.vue';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AutomationActionTeamMessageInput,
|
||||
AutomationActionFileInput,
|
||||
WootMessageEditor,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
@@ -172,11 +175,11 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-if="!isMacro"
|
||||
icon="dismiss"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="i-lucide-x"
|
||||
slate
|
||||
ghost
|
||||
class="flex-shrink-0"
|
||||
@click="removeAction"
|
||||
/>
|
||||
|
||||
@@ -32,7 +32,7 @@ const buttonStyleClass = props.compact ? 'text-sm' : 'text-base';
|
||||
:class="buttonStyleClass"
|
||||
@click.capture="goBack"
|
||||
>
|
||||
<fluent-icon icon="chevron-left" class="-ml-1" />
|
||||
<i class="i-lucide-chevron-left -ml-1 text-lg" />
|
||||
{{ buttonLabel || $t('GENERAL_SETTINGS.BACK') }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
<script>
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
name: 'FilterInput',
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
@@ -242,10 +247,11 @@ export default {
|
||||
:placeholder="$t('FILTER.INPUT_PLACEHOLDER')"
|
||||
/>
|
||||
</div>
|
||||
<woot-button
|
||||
icon="dismiss"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
<NextButton
|
||||
icon="i-lucide-x"
|
||||
slate
|
||||
ghost
|
||||
class="flex-shrink-0"
|
||||
@click="removeFilter"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
currentPage: {
|
||||
type: Number,
|
||||
@@ -21,13 +21,6 @@ const hasFirstPage = computed(() => props.currentPage === 1);
|
||||
const hasNextPage = computed(() => props.currentPage === props.totalPages);
|
||||
const hasPrevPage = computed(() => props.currentPage === 1);
|
||||
|
||||
function buttonClass(hasPage) {
|
||||
if (hasPage) {
|
||||
return 'hover:!bg-slate-50 dark:hover:!bg-slate-800';
|
||||
}
|
||||
return 'dark:hover:!bg-slate-700/50';
|
||||
}
|
||||
|
||||
function onPageChange(newPage) {
|
||||
emit('pageChange', newPage);
|
||||
}
|
||||
@@ -55,84 +48,61 @@ const onLastPage = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center h-8 rounded-lg bg-slate-50 dark:bg-slate-800">
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
:is-disabled="hasFirstPage"
|
||||
class-names="dark:!bg-slate-800 !opacity-100 ltr:rounded-l-lg ltr:rounded-r-none rtl:rounded-r-lg rtl:rounded-l-none"
|
||||
:class="buttonClass(hasFirstPage)"
|
||||
<div
|
||||
class="flex items-center h-8 outline outline-1 outline-n-weak rounded-lg"
|
||||
>
|
||||
<NextButton
|
||||
faded
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-chevrons-left"
|
||||
class="ltr:rounded-l-lg ltr:rounded-r-none rtl:rounded-r-lg rtl:rounded-l-none"
|
||||
:disabled="hasFirstPage"
|
||||
@click="onFirstPage"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="chevrons-left"
|
||||
size="20"
|
||||
icon-lib="lucide"
|
||||
:class="hasFirstPage && 'opacity-40'"
|
||||
/>
|
||||
</woot-button>
|
||||
<div class="w-px h-4 rounded-sm bg-slate-75 dark:bg-slate-700/50" />
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
:is-disabled="hasPrevPage"
|
||||
class-names="dark:!bg-slate-800 !opacity-100 rounded-none"
|
||||
:class="buttonClass(hasPrevPage)"
|
||||
/>
|
||||
<div class="flex items-center justify-center bg-n-slate-9/10 h-full">
|
||||
<div class="w-px h-4 rounded-sm bg-n-strong" />
|
||||
</div>
|
||||
<NextButton
|
||||
faded
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-chevron-left"
|
||||
class="rounded-none"
|
||||
:disabled="hasPrevPage"
|
||||
@click="onPrevPage"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="chevron-left-single"
|
||||
size="20"
|
||||
icon-lib="lucide"
|
||||
:class="hasPrevPage && 'opacity-40'"
|
||||
/>
|
||||
</woot-button>
|
||||
|
||||
/>
|
||||
<div
|
||||
class="flex items-center gap-3 px-3 tabular-nums bg-slate-50 dark:bg-slate-800 text-slate-700 dark:text-slate-100"
|
||||
class="flex items-center gap-3 px-3 tabular-nums bg-n-slate-9/10 h-full"
|
||||
>
|
||||
<span class="text-sm text-slate-800 dark:text-slate-75">
|
||||
<span class="text-sm text-n-slate-12">
|
||||
{{ currentPage }}
|
||||
</span>
|
||||
<span class="text-slate-600 dark:text-slate-500">/</span>
|
||||
<span class="text-sm text-slate-600 dark:text-slate-500">
|
||||
<span class="text-n-slate-11">/</span>
|
||||
<span class="text-sm text-n-slate-11">
|
||||
{{ totalPages }}
|
||||
</span>
|
||||
</div>
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
:is-disabled="hasNextPage"
|
||||
class-names="dark:!bg-slate-800 !opacity-100 rounded-none"
|
||||
:class="buttonClass(hasNextPage)"
|
||||
<NextButton
|
||||
faded
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-chevron-right"
|
||||
class="rounded-none"
|
||||
:disabled="hasNextPage"
|
||||
@click="onNextPage"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="chevron-right-single"
|
||||
size="20"
|
||||
icon-lib="lucide"
|
||||
:class="hasNextPage && 'opacity-40'"
|
||||
/>
|
||||
</woot-button>
|
||||
<div class="w-px h-4 rounded-sm bg-slate-75 dark:bg-slate-700/50" />
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
class-names="dark:!bg-slate-800 !opacity-100 ltr:rounded-r-lg ltr:rounded-l-none rtl:rounded-l-lg rtl:rounded-r-none"
|
||||
:class="buttonClass(hasLastPage)"
|
||||
:is-disabled="hasLastPage"
|
||||
/>
|
||||
<div class="flex items-center justify-center bg-n-slate-9/10 h-full">
|
||||
<div class="w-px h-4 rounded-sm bg-n-strong" />
|
||||
</div>
|
||||
<NextButton
|
||||
faded
|
||||
sm
|
||||
slate
|
||||
icon="i-lucide-chevrons-right"
|
||||
class="ltr:rounded-r-lg ltr:rounded-l-none rtl:rounded-l-lg rtl:rounded-r-none"
|
||||
:disabled="hasLastPage"
|
||||
@click="onLastPage"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="chevrons-right"
|
||||
size="20"
|
||||
icon-lib="lucide"
|
||||
:class="hasLastPage && 'opacity-40'"
|
||||
/>
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -164,11 +164,6 @@ export default {
|
||||
'is-note-mode': this.isNote,
|
||||
};
|
||||
},
|
||||
buttonClass() {
|
||||
return {
|
||||
warning: this.isNote,
|
||||
};
|
||||
},
|
||||
showAttachButton() {
|
||||
return this.showFileUpload || this.isNote;
|
||||
},
|
||||
@@ -367,14 +362,15 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
<div class="right-wrap">
|
||||
<woot-button
|
||||
size="small"
|
||||
:class-names="buttonClass"
|
||||
:is-disabled="isSendDisabled"
|
||||
<NextButton
|
||||
:label="sendButtonText"
|
||||
type="submit"
|
||||
sm
|
||||
:color="isNote ? 'amber' : 'blue'"
|
||||
:disabled="isSendDisabled"
|
||||
class="flex-shrink-0"
|
||||
@click="onSend"
|
||||
>
|
||||
{{ sendButtonText }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -106,43 +106,3 @@ export default {
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.button-group {
|
||||
@apply flex border-0 p-0 m-0;
|
||||
|
||||
.button {
|
||||
@apply text-sm font-medium py-2.5 px-4 m-0 relative z-10;
|
||||
|
||||
&.is-active {
|
||||
@apply bg-white dark:bg-slate-900;
|
||||
}
|
||||
}
|
||||
|
||||
.button--reply {
|
||||
@apply border-r rounded-none border-b-0 border-l-0 border-t-0 border-slate-50 dark:border-slate-700;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply border-r border-slate-50 dark:border-slate-700;
|
||||
}
|
||||
}
|
||||
|
||||
.button--note {
|
||||
@apply border-l-0 rounded-none;
|
||||
|
||||
&.is-active {
|
||||
@apply border-r border-b-0 bg-yellow-100 dark:bg-yellow-800 border-t-0 border-slate-50 dark:border-slate-700;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
@apply text-yellow-700 dark:text-yellow-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button--note {
|
||||
@apply text-yellow-600 dark:text-yellow-600 bg-transparent dark:bg-transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,6 +13,8 @@ import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import Linear from './linear/index.vue';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BackButton,
|
||||
@@ -21,6 +23,7 @@ export default {
|
||||
Thumbnail,
|
||||
SLACardLabel,
|
||||
Linear,
|
||||
NextButton,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
@@ -154,16 +157,13 @@ export default {
|
||||
<div
|
||||
class="flex flex-row items-center max-w-full gap-1 p-0 m-0 w-fit"
|
||||
>
|
||||
<woot-button
|
||||
variant="link"
|
||||
color-scheme="secondary"
|
||||
class="[&>span]:overflow-hidden [&>span]:whitespace-nowrap [&>span]:text-ellipsis min-w-0"
|
||||
@click.prevent="$emit('contactPanelToggle')"
|
||||
>
|
||||
<span class="text-base font-medium leading-tight text-n-slate-12">
|
||||
<NextButton link slate @click.prevent="$emit('contactPanelToggle')">
|
||||
<span
|
||||
class="text-base font-medium truncate leading-tight text-n-slate-12"
|
||||
>
|
||||
{{ currentContact.name }}
|
||||
</span>
|
||||
</woot-button>
|
||||
</NextButton>
|
||||
<fluent-icon
|
||||
v-if="!isHMACVerified"
|
||||
v-tooltip="$t('CONVERSATION.UNVERIFIED_SESSION')"
|
||||
@@ -180,14 +180,13 @@ export default {
|
||||
<span v-if="isSnoozed" class="font-medium text-n-amber-10">
|
||||
{{ snoozedDisplayText }}
|
||||
</span>
|
||||
<woot-button
|
||||
class="p-0"
|
||||
size="small"
|
||||
variant="link"
|
||||
<NextButton
|
||||
link
|
||||
xs
|
||||
blue
|
||||
:label="contactPanelToggleText"
|
||||
@click="$emit('contactPanelToggle')"
|
||||
>
|
||||
{{ contactPanelToggleText }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { required, minLength, email } from '@vuelidate/validators';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
@@ -153,13 +157,18 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
|
||||
<woot-submit-button
|
||||
:button-text="$t('EMAIL_TRANSCRIPT.SUBMIT')"
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('EMAIL_TRANSCRIPT.CANCEL')"
|
||||
@click.prevent="onCancel"
|
||||
/>
|
||||
<NextButton
|
||||
type="submit"
|
||||
:label="$t('EMAIL_TRANSCRIPT.SUBMIT')"
|
||||
:disabled="!isFormValid"
|
||||
/>
|
||||
<button class="button clear" @click.prevent="onCancel">
|
||||
{{ $t('EMAIL_TRANSCRIPT.CANCEL') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,8 @@ import * as Sentry from '@sentry/vue';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BubbleActions,
|
||||
@@ -40,6 +42,7 @@ export default {
|
||||
InstagramStory,
|
||||
InstagramStoryReply,
|
||||
Spinner,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
@@ -452,12 +455,12 @@ export default {
|
||||
v-if="isFailed && !hasOneDayPassed && !isAnEmailInbox"
|
||||
class="message-failed--alert"
|
||||
>
|
||||
<woot-button
|
||||
<NextButton
|
||||
v-tooltip.top-end="$t('CONVERSATION.TRY_AGAIN')"
|
||||
size="tiny"
|
||||
color-scheme="alert"
|
||||
variant="clear"
|
||||
icon="arrow-clockwise"
|
||||
ghost
|
||||
xs
|
||||
ruby
|
||||
icon="i-lucide-refresh-ccw"
|
||||
@click="retrySendMessage"
|
||||
/>
|
||||
</div>
|
||||
@@ -660,10 +663,10 @@ export default {
|
||||
}
|
||||
|
||||
&.is-failed {
|
||||
@apply bg-red-200 dark:bg-red-200;
|
||||
@apply bg-n-ruby-4 dark:bg-n-ruby-4 text-n-slate-12;
|
||||
|
||||
.message-text--metadata .time {
|
||||
@apply text-red-50 dark:text-red-50;
|
||||
@apply text-n-ruby-12 dark:text-n-ruby-12;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,7 +727,7 @@ li.right {
|
||||
}
|
||||
|
||||
.wrap.is-failed {
|
||||
@apply flex items-end ml-auto;
|
||||
@apply flex items-end ltr:ml-auto rtl:mr-auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -1,10 +1,11 @@
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
openProfileSettings() {
|
||||
return this.$router.push({ name: 'profile_settings_index' });
|
||||
},
|
||||
},
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const openProfileSettings = () => {
|
||||
return router.push({ name: 'profile_settings_index' });
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -14,13 +15,12 @@ export default {
|
||||
>
|
||||
<p class="w-fit !m-0">
|
||||
{{ $t('CONVERSATION.FOOTER.MESSAGE_SIGNATURE_NOT_CONFIGURED') }}
|
||||
<woot-button
|
||||
color-scheme="primary"
|
||||
variant="link"
|
||||
|
||||
<Button
|
||||
link
|
||||
:label="$t('CONVERSATION.FOOTER.CLICK_HERE')"
|
||||
@click="openProfileSettings"
|
||||
>
|
||||
{{ $t('CONVERSATION.FOOTER.CLICK_HERE') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -39,6 +39,8 @@ import wootConstants from 'dashboard/constants/globals';
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { FEATURE_FLAGS } from '../../../featureFlags';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Message,
|
||||
@@ -48,6 +50,7 @@ export default {
|
||||
UnreadIndicator,
|
||||
Banner,
|
||||
ConversationLabelSuggestion,
|
||||
NextButton,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
@@ -560,13 +563,15 @@ export default {
|
||||
:href-link-text="replyWindowLinkText"
|
||||
/>
|
||||
<div class="flex justify-end">
|
||||
<woot-button
|
||||
variant="smooth"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
class="box-border fixed z-10 bg-white border border-r-0 border-solid rounded-bl-calc rtl:rotate-180 rounded-tl-calc border-n-weak"
|
||||
<NextButton
|
||||
faded
|
||||
xs
|
||||
slate
|
||||
class="!rounded-r-none rtl:rotate-180 !rounded-2xl !fixed z-10"
|
||||
:icon="
|
||||
isContactPanelOpen ? 'i-ph-caret-right-fill' : 'i-ph-caret-left-fill'
|
||||
"
|
||||
:class="isInboxView ? 'top-52 md:top-40' : 'top-32'"
|
||||
:icon="isRightOrLeftIcon"
|
||||
@click="onToggleContactPanel"
|
||||
/>
|
||||
</div>
|
||||
@@ -684,20 +689,6 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.rounded-bl-calc {
|
||||
border-bottom-left-radius: calc(1.5rem + 1px);
|
||||
}
|
||||
|
||||
.rounded-tl-calc {
|
||||
border-top-left-radius: calc(1.5rem + 1px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.modal-mask {
|
||||
@apply absolute;
|
||||
|
||||
@@ -420,6 +420,7 @@ export default {
|
||||
if (conversationId !== oldConversationId) {
|
||||
this.setToDraft(oldConversationId, this.replyType);
|
||||
this.getFromDraft();
|
||||
this.resetRecorderAndClearAttachments();
|
||||
}
|
||||
},
|
||||
message(updatedMessage) {
|
||||
@@ -529,6 +530,12 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
resetRecorderAndClearAttachments() {
|
||||
// Reset audio recorder UI state
|
||||
this.resetAudioRecorderInput();
|
||||
// Reset attached files
|
||||
this.attachedFiles = [];
|
||||
},
|
||||
saveDraft(conversationId, replyType) {
|
||||
if (this.message || this.message === '') {
|
||||
const key = `draft-${conversationId}-${replyType}`;
|
||||
@@ -1067,7 +1074,7 @@ export default {
|
||||
<template>
|
||||
<Banner
|
||||
v-if="showSelfAssignBanner"
|
||||
action-button-variant="clear"
|
||||
action-button-variant="ghost"
|
||||
color-scheme="secondary"
|
||||
class="banner--self-assign mx-2 mb-2 rounded-lg"
|
||||
:banner-message="$t('CONVERSATION.NOT_ASSIGNED_TO_YOU')"
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script>
|
||||
<script setup>
|
||||
import MessagePreview from 'dashboard/components/widgets/conversation/MessagePreview.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: { MessagePreview },
|
||||
props: {
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
defineProps({
|
||||
message: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
emits: ['dismiss'],
|
||||
};
|
||||
});
|
||||
|
||||
const emit = defineEmits(['dismiss']);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="reply-editor bg-slate-50 dark:bg-slate-800 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2"
|
||||
class="reply-editor bg-n-slate-9/10 rounded-md py-1 pl-2 pr-1 text-xs tracking-wide mt-2 flex items-center gap-1.5 -mx-2"
|
||||
>
|
||||
<fluent-icon class="flex-shrink-0 icon" icon="arrow-reply" size="14" />
|
||||
<div class="flex-grow gap-1 mt-px text-xs truncate">
|
||||
@@ -27,14 +26,13 @@ export default {
|
||||
class="inline"
|
||||
/>
|
||||
</div>
|
||||
<woot-button
|
||||
<Button
|
||||
v-tooltip="$t('CONVERSATION.REPLYBOX.DISMISS_REPLY')"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
variant="clear"
|
||||
size="tiny"
|
||||
class="flex-shrink-0"
|
||||
@click.stop="$emit('dismiss')"
|
||||
ghost
|
||||
xs
|
||||
slate
|
||||
icon="i-lucide-x"
|
||||
@click.stop="emit('dismiss')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+29
-55
@@ -12,7 +12,12 @@ import { ref, computed, onMounted } from 'vue';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { requiredIf } from '@vuelidate/validators';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
template: {
|
||||
type: Object,
|
||||
@@ -114,78 +119,47 @@ export default {
|
||||
readonly
|
||||
class="template-input"
|
||||
/>
|
||||
<div v-if="variables" class="template__variables-container">
|
||||
<p class="variables-label">
|
||||
<div v-if="variables" class="p-2.5">
|
||||
<p class="text-sm font-semibold mb-2.5">
|
||||
{{ $t('WHATSAPP_TEMPLATES.PARSER.VARIABLES_LABEL') }}
|
||||
</p>
|
||||
<div
|
||||
v-for="(variable, key) in processedParams"
|
||||
:key="key"
|
||||
class="template__variable-item"
|
||||
class="items-center flex mb-2.5"
|
||||
>
|
||||
<span class="variable-label">
|
||||
<span
|
||||
class="bg-n-alpha-black2 text-n-slate-12 inline-block rounded-md text-xs py-2.5 px-6"
|
||||
>
|
||||
{{ key }}
|
||||
</span>
|
||||
<woot-input
|
||||
v-model="processedParams[key]"
|
||||
type="text"
|
||||
class="variable-input"
|
||||
class="flex-1 text-sm ml-2.5"
|
||||
:styles="{ marginBottom: 0 }"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="v$.$dirty && v$.$invalid" class="error">
|
||||
<p
|
||||
v-if="v$.$dirty && v$.$invalid"
|
||||
class="bg-n-ruby-9/20 rounded-md text-n-ruby-9 p-2.5 text-center"
|
||||
>
|
||||
{{ $t('WHATSAPP_TEMPLATES.PARSER.FORM_ERROR_MESSAGE') }}
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<woot-button variant="smooth" @click="resetTemplate">
|
||||
{{ $t('WHATSAPP_TEMPLATES.PARSER.GO_BACK_LABEL') }}
|
||||
</woot-button>
|
||||
<woot-button type="button" @click="sendMessage">
|
||||
{{ $t('WHATSAPP_TEMPLATES.PARSER.SEND_MESSAGE_LABEL') }}
|
||||
</woot-button>
|
||||
<footer class="flex justify-end gap-2">
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('WHATSAPP_TEMPLATES.PARSER.GO_BACK_LABEL')"
|
||||
@click="resetTemplate"
|
||||
/>
|
||||
<NextButton
|
||||
type="button"
|
||||
:label="$t('WHATSAPP_TEMPLATES.PARSER.SEND_MESSAGE_LABEL')"
|
||||
@click="sendMessage"
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.template__variables-container {
|
||||
@apply p-2.5;
|
||||
}
|
||||
|
||||
.variables-label {
|
||||
@apply text-sm font-semibold mb-2.5;
|
||||
}
|
||||
|
||||
.template__variable-item {
|
||||
@apply items-center flex mb-2.5;
|
||||
|
||||
.label {
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.variable-input {
|
||||
@apply flex-1 text-sm ml-2.5;
|
||||
}
|
||||
|
||||
.variable-label {
|
||||
@apply bg-slate-75 dark:bg-slate-700 text-slate-700 dark:text-slate-100 inline-block rounded-md text-xs py-2.5 px-6;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
@apply flex justify-end;
|
||||
|
||||
button {
|
||||
@apply ml-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
@apply bg-red-100 dark:bg-red-100 rounded-md text-red-800 dark:text-red-800 p-2.5 text-center;
|
||||
}
|
||||
|
||||
.template-input {
|
||||
@apply bg-slate-25 dark:bg-slate-900 text-slate-700 dark:text-slate-100;
|
||||
}
|
||||
</style>
|
||||
|
||||
+21
-52
@@ -43,48 +43,56 @@ export default {
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="gap-1 templates__list-search">
|
||||
<fluent-icon icon="search" class="search-icon" size="16" />
|
||||
<div
|
||||
class="gap-1 bg-n-alpha-black2 items-center flex mb-2.5 py-0 px-2.5 rounded-lg outline outline-1 outline-n-weak hover:outline-n-slate-6 dark:hover:outline-n-slate-6 focus-within:outline-n-brand dark:focus-within:outline-n-brand"
|
||||
>
|
||||
<fluent-icon icon="search" class="text-n-slate-12" size="16" />
|
||||
<input
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="$t('WHATSAPP_TEMPLATES.PICKER.SEARCH_PLACEHOLDER')"
|
||||
class="templates__search-input"
|
||||
class="reset-base w-full h-9 bg-transparent text-n-slate-12 !text-sm !outline-0"
|
||||
/>
|
||||
</div>
|
||||
<div class="template__list-container">
|
||||
<div
|
||||
class="bg-n-background outline-n-container outline outline-1 rounded-lg max-h-[18.75rem] overflow-y-auto p-2.5"
|
||||
>
|
||||
<div v-for="(template, i) in filteredTemplateMessages" :key="template.id">
|
||||
<button
|
||||
class="template__list-item"
|
||||
class="rounded-lg cursor-pointer block p-2.5 text-left w-full hover:bg-n-alpha-2 dark:hover:bg-n-solid-2"
|
||||
@click="$emit('onSelect', template)"
|
||||
>
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2.5">
|
||||
<p class="label-title">
|
||||
<p class="text-sm">
|
||||
{{ template.name }}
|
||||
</p>
|
||||
<span
|
||||
class="inline-block px-2 py-1 text-xs leading-none bg-white rounded-sm cursor-default dark:bg-slate-700 text-slate-800 dark:text-slate-100"
|
||||
class="inline-block px-2 py-1 text-xs leading-none bg-n-slate-3 rounded-lg cursor-default text-n-slate-12"
|
||||
>
|
||||
{{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.LANGUAGE') }} :
|
||||
{{ template.language }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<p class="strong">
|
||||
<p class="font-medium">
|
||||
{{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.TEMPLATE_BODY') }}
|
||||
</p>
|
||||
<p class="label-body">{{ getTemplatebody(template) }}</p>
|
||||
</div>
|
||||
<div class="label-category">
|
||||
<p class="strong">
|
||||
<div class="mt-5">
|
||||
<p class="font-medium">
|
||||
{{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.CATEGORY') }}
|
||||
</p>
|
||||
<p>{{ template.category }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<hr v-if="i != filteredTemplateMessages.length - 1" :key="`hr-${i}`" />
|
||||
<hr
|
||||
v-if="i != filteredTemplateMessages.length - 1"
|
||||
:key="`hr-${i}`"
|
||||
class="border-b border-solid border-n-weak my-2.5 mx-auto max-w-[95%]"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!filteredTemplateMessages.length">
|
||||
<p>
|
||||
@@ -97,46 +105,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.templates__list-search {
|
||||
@apply items-center flex bg-slate-25 dark:bg-slate-900 mb-2.5 py-0 px-2.5 rounded-md border border-solid border-slate-100 dark:border-slate-700;
|
||||
|
||||
.search-icon {
|
||||
@apply text-slate-400 dark:text-slate-300;
|
||||
}
|
||||
|
||||
.templates__search-input {
|
||||
@apply bg-transparent border-0 text-xs h-9 m-0;
|
||||
}
|
||||
}
|
||||
.template__list-container {
|
||||
@apply bg-slate-25 dark:bg-slate-900 rounded-md max-h-[18.75rem] overflow-y-auto p-2.5;
|
||||
|
||||
.template__list-item {
|
||||
@apply rounded-lg cursor-pointer block p-2.5 text-left w-full hover:bg-woot-50 dark:hover:bg-slate-800;
|
||||
|
||||
.label-title {
|
||||
@apply text-sm;
|
||||
}
|
||||
|
||||
.label-category {
|
||||
@apply mt-5;
|
||||
|
||||
span {
|
||||
@apply text-sm font-semibold;
|
||||
}
|
||||
}
|
||||
|
||||
.label-body {
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.strong {
|
||||
@apply text-xs font-semibold;
|
||||
}
|
||||
|
||||
hr {
|
||||
@apply border-b border-solid border-slate-100 dark:border-slate-700 my-2.5 mx-auto max-w-[95%];
|
||||
.label-body {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -22,6 +22,14 @@ const filterTypes = [
|
||||
filterOperators: OPERATOR_TYPES_2,
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'priority',
|
||||
attributeI18nKey: 'PRIORITY',
|
||||
inputType: 'multi_select',
|
||||
dataType: 'text',
|
||||
filterOperators: OPERATOR_TYPES_1,
|
||||
attributeModel: 'standard',
|
||||
},
|
||||
{
|
||||
attributeKey: 'inbox_id',
|
||||
attributeI18nKey: 'INBOX_NAME',
|
||||
|
||||
@@ -4,8 +4,12 @@ import {
|
||||
DuplicateContactException,
|
||||
ExceptionWithMessage,
|
||||
} from 'shared/helpers/CustomErrors';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
@@ -91,9 +95,12 @@ export default {
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="formattedPhoneNumber" class="link-wrap">
|
||||
<woot-button variant="clear" size="small" @click.prevent="addContact">
|
||||
{{ $t('CONVERSATION.SAVE_CONTACT') }}
|
||||
</woot-button>
|
||||
<NextButton
|
||||
ghost
|
||||
xs
|
||||
:label="$t('CONVERSATION.SAVE_CONTACT')"
|
||||
@click.prevent="addContact"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+15
-17
@@ -2,8 +2,12 @@
|
||||
import DyteAPI from 'dashboard/api/integrations/dyte';
|
||||
import { buildDyteURL } from 'shared/helpers/IntegrationHelper';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
messageId: {
|
||||
type: Number,
|
||||
@@ -41,31 +45,25 @@ export default {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
icon="video-add"
|
||||
class="join-call-button"
|
||||
<NextButton
|
||||
blue
|
||||
sm
|
||||
icon="i-lucide-video"
|
||||
:label="$t('INTEGRATION_SETTINGS.DYTE.CLICK_HERE_TO_JOIN')"
|
||||
:is-loading="isLoading"
|
||||
@click="joinTheCall"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DYTE.CLICK_HERE_TO_JOIN') }}
|
||||
</woot-button>
|
||||
/>
|
||||
<div v-if="dyteAuthToken" class="video-call--container">
|
||||
<iframe
|
||||
:src="meetingLink"
|
||||
allow="camera;microphone;fullscreen;display-capture;picture-in-picture;clipboard-write;"
|
||||
/>
|
||||
<woot-button
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="secondary"
|
||||
class="join-call-button"
|
||||
<NextButton
|
||||
sm
|
||||
class="mt-2"
|
||||
:label="$t('INTEGRATION_SETTINGS.DYTE.LEAVE_THE_ROOM')"
|
||||
@click="leaveTheRoom"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.DYTE.LEAVE_THE_ROOM') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+11
-11
@@ -3,6 +3,7 @@ import { ref, computed } from 'vue';
|
||||
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import SLAEventItem from './SLAEventItem.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
slaMissedEvents: {
|
||||
@@ -60,20 +61,19 @@ const toggleShowAllNRT = () => {
|
||||
v-if="shouldShowMoreNRTButton"
|
||||
class="flex flex-col items-end w-full"
|
||||
>
|
||||
<woot-button
|
||||
size="small"
|
||||
:icon="!shouldShowAllNrts ? 'plus-sign' : ''"
|
||||
variant="link"
|
||||
color-scheme="secondary"
|
||||
class="hover:!no-underline !gap-1 hover:!bg-transparent dark:hover:!bg-transparent"
|
||||
@click="toggleShowAllNRT"
|
||||
>
|
||||
{{
|
||||
<Button
|
||||
link
|
||||
xs
|
||||
slate
|
||||
class="hover:!no-underline"
|
||||
:icon="!shouldShowAllNrts ? 'i-lucide-plus' : ''"
|
||||
:label="
|
||||
shouldShowAllNrts
|
||||
? $t('SLA.EVENTS.HIDE', { count: nrtMisses.length })
|
||||
: $t('SLA.EVENTS.SHOW_MORE', { count: nrtMisses.length })
|
||||
}}
|
||||
</woot-button>
|
||||
"
|
||||
@click="toggleShowAllNRT"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</SLAEventItem>
|
||||
|
||||
+25
-23
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
// components
|
||||
import WootButton from '../../../ui/WootButton.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
import Avatar from '../../Avatar.vue';
|
||||
|
||||
// composables
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
name: 'LabelSuggestion',
|
||||
components: {
|
||||
Avatar,
|
||||
WootButton,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
suggestedLabels: {
|
||||
@@ -185,42 +185,44 @@ export default {
|
||||
"
|
||||
/>
|
||||
</button>
|
||||
<WootButton
|
||||
<NextButton
|
||||
v-if="preparedLabels.length === 1"
|
||||
v-tooltip.top="{
|
||||
content: $t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.DISMISS'),
|
||||
delay: { show: 600, hide: 0 },
|
||||
hideOnClick: true,
|
||||
}"
|
||||
variant="smooth"
|
||||
:color-scheme="isHovered ? 'alert' : 'primary'"
|
||||
class="label--add"
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
faded
|
||||
xs
|
||||
icon="i-lucide-x"
|
||||
class="flex-shrink-0"
|
||||
:color="isHovered ? 'ruby' : 'blue'"
|
||||
@click="dismissSuggestions"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="preparedLabels.length > 1">
|
||||
<WootButton
|
||||
:variant="selectedLabels.length === 0 ? 'smooth' : ''"
|
||||
class="label--add"
|
||||
icon="add"
|
||||
size="tiny"
|
||||
<div
|
||||
v-if="preparedLabels.length > 1"
|
||||
class="inline-flex items-center gap-1"
|
||||
>
|
||||
<NextButton
|
||||
xs
|
||||
icon="i-lucide-plus"
|
||||
class="flex-shrink-0"
|
||||
:variant="selectedLabels.length === 0 ? 'faded' : 'solid'"
|
||||
:label="addButtonText"
|
||||
@click="addAllLabels"
|
||||
>
|
||||
{{ addButtonText }}
|
||||
</WootButton>
|
||||
<WootButton
|
||||
/>
|
||||
<NextButton
|
||||
v-tooltip.top="{
|
||||
content: $t('LABEL_MGMT.SUGGESTIONS.TOOLTIP.DISMISS'),
|
||||
delay: { show: 600, hide: 0 },
|
||||
hideOnClick: true,
|
||||
}"
|
||||
:color-scheme="isHovered ? 'alert' : 'primary'"
|
||||
variant="smooth"
|
||||
class="label--add"
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
faded
|
||||
xs
|
||||
icon="i-lucide-x"
|
||||
class="flex-shrink-0"
|
||||
:color="isHovered ? 'ruby' : 'blue'"
|
||||
@click="dismissSuggestions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
+16
-20
@@ -2,11 +2,13 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Thumbnail,
|
||||
Spinner,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
selectedInboxes: {
|
||||
@@ -94,13 +96,7 @@ export default {
|
||||
</div>
|
||||
<div class="flex items-center justify-between header">
|
||||
<span>{{ $t('BULK_ACTION.AGENT_SELECT_LABEL') }}</span>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
@click="onClose"
|
||||
/>
|
||||
<NextButton ghost xs slate icon="i-lucide-x" @click="onClose" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<div
|
||||
@@ -121,7 +117,7 @@ export default {
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="$t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
class="agent--search_input"
|
||||
class="reset-base !outline-0 !text-sm agent--search_input"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
@@ -161,21 +157,21 @@ export default {
|
||||
}}
|
||||
</p>
|
||||
<div class="agent-confirmation-actions">
|
||||
<woot-button
|
||||
color-scheme="primary"
|
||||
variant="smooth"
|
||||
<NextButton
|
||||
faded
|
||||
sm
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('BULK_ACTION.GO_BACK_LABEL')"
|
||||
@click="goBack"
|
||||
>
|
||||
{{ $t('BULK_ACTION.GO_BACK_LABEL') }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
color-scheme="primary"
|
||||
variant="flat"
|
||||
/>
|
||||
<NextButton
|
||||
sm
|
||||
type="submit"
|
||||
:label="$t('BULK_ACTION.YES')"
|
||||
:is-loading="uiFlags.isUpdating"
|
||||
@click="submit"
|
||||
>
|
||||
{{ $t('BULK_ACTION.YES') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+11
-15
@@ -1,7 +1,11 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
emits: ['update', 'close', 'assign'],
|
||||
data() {
|
||||
return {
|
||||
@@ -40,13 +44,7 @@ export default {
|
||||
</div>
|
||||
<div class="flex items-center justify-between header">
|
||||
<span>{{ $t('BULK_ACTION.LABELS.ASSIGN_LABELS') }}</span>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
@click="onClose"
|
||||
/>
|
||||
<NextButton ghost xs slate icon="i-lucide-x" @click="onClose" />
|
||||
</div>
|
||||
<div class="labels-list">
|
||||
<header class="labels-list__header">
|
||||
@@ -58,7 +56,7 @@ export default {
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="$t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
class="label--search_input"
|
||||
class="reset-base !outline-0 !text-sm label--search_input"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
@@ -91,15 +89,13 @@ export default {
|
||||
</li>
|
||||
</ul>
|
||||
<footer class="labels-list__footer">
|
||||
<woot-button
|
||||
size="small"
|
||||
is-expanded
|
||||
color-scheme="primary"
|
||||
<NextButton
|
||||
sm
|
||||
type="submit"
|
||||
:label="$t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS')"
|
||||
:disabled="!selectedLabels.length"
|
||||
@click="$emit('assign', selectedLabels)"
|
||||
>
|
||||
<span>{{ $t('BULK_ACTION.LABELS.ASSIGN_SELECTED_LABELS') }}</span>
|
||||
</woot-button>
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+7
-8
@@ -1,6 +1,11 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
emits: ['assignTeam', 'close'],
|
||||
|
||||
data() {
|
||||
@@ -40,13 +45,7 @@ export default {
|
||||
</div>
|
||||
<div class="flex items-center justify-between header">
|
||||
<span>{{ $t('BULK_ACTION.TEAMS.TEAM_SELECT_LABEL') }}</span>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
@click="onClose"
|
||||
/>
|
||||
<NextButton ghost xs slate icon="i-lucide-x" @click="onClose" />
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="team__list-container">
|
||||
@@ -60,7 +59,7 @@ export default {
|
||||
v-model="query"
|
||||
type="search"
|
||||
:placeholder="$t('BULK_ACTION.SEARCH_INPUT_PLACEHOLDER')"
|
||||
class="agent--search_input"
|
||||
class="reset-base !outline-0 !text-sm agent--search_input"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
+12
-18
@@ -4,6 +4,7 @@ import { ref } from 'vue';
|
||||
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
showResolve: {
|
||||
@@ -25,9 +26,9 @@ const emit = defineEmits(['update', 'close']);
|
||||
const { t } = useI18n();
|
||||
|
||||
const actions = ref([
|
||||
{ icon: 'checkmark', key: 'resolved' },
|
||||
{ icon: 'arrow-redo', key: 'open' },
|
||||
{ icon: 'send-clock', key: 'snoozed' },
|
||||
{ icon: 'i-lucide-check', key: 'resolved' },
|
||||
{ icon: 'i-lucide-redo', key: 'open' },
|
||||
{ icon: 'i-lucide-alarm-clock', key: 'snoozed' },
|
||||
]);
|
||||
|
||||
const updateConversations = key => {
|
||||
@@ -85,28 +86,21 @@ const actionLabel = key => {
|
||||
<span class="text-sm font-medium text-slate-600 dark:text-slate-100">
|
||||
{{ $t('BULK_ACTION.UPDATE.CHANGE_STATUS') }}
|
||||
</span>
|
||||
<woot-button
|
||||
size="tiny"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss"
|
||||
@click="onClose"
|
||||
/>
|
||||
<Button ghost xs slate icon="i-lucide-x" @click="onClose" />
|
||||
</div>
|
||||
<div class="px-2.5 pt-0 pb-2.5">
|
||||
<WootDropdownMenu class="m-0 list-none">
|
||||
<template v-for="action in actions">
|
||||
<WootDropdownItem v-if="showAction(action.key)" :key="action.key">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
size="small"
|
||||
<Button
|
||||
ghost
|
||||
sm
|
||||
slate
|
||||
class="!w-full ltr:!justify-start rtl:!justify-end"
|
||||
:icon="action.icon"
|
||||
class="hover:!bg-n-slate-3 dark:hover:!bg-n-solid-3"
|
||||
:label="actionLabel(action.key)"
|
||||
@click="updateConversations(action.key)"
|
||||
>
|
||||
{{ actionLabel(action.key) }}
|
||||
</woot-button>
|
||||
/>
|
||||
</WootDropdownItem>
|
||||
</template>
|
||||
</WootDropdownMenu>
|
||||
|
||||
@@ -9,6 +9,7 @@ import validations from './validations';
|
||||
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||
import SearchableDropdown from './SearchableDropdown.vue';
|
||||
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
conversationId: {
|
||||
@@ -248,20 +249,20 @@ onMounted(getTeams);
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center justify-end w-full gap-2 mt-8">
|
||||
<woot-button
|
||||
class="px-4 rounded-xl button clear outline-woot-200/50 outline"
|
||||
<Button
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CANCEL')"
|
||||
@click.prevent="onClose"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CANCEL') }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
:is-disabled="isSubmitDisabled"
|
||||
class="px-4 rounded-xl"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
:label="$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE')"
|
||||
:disabled="isSubmitDisabled"
|
||||
:is-loading="isCreating"
|
||||
@click.prevent="createIssue"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CREATE') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup>
|
||||
import { inject } from 'vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
identifier: {
|
||||
type: String,
|
||||
@@ -37,30 +39,24 @@ const openIssue = () => {
|
||||
/>
|
||||
<span class="text-xs font-medium text-ash-900">{{ identifier }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-0.5">
|
||||
<woot-button
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
class="h-[24px]"
|
||||
<div class="flex items-center gap-1">
|
||||
<Button
|
||||
ghost
|
||||
xs
|
||||
slate
|
||||
icon="i-lucide-unlink"
|
||||
class="!transition-none"
|
||||
:is-loading="isUnlinking"
|
||||
@click="unlinkIssue"
|
||||
>
|
||||
<fluent-icon
|
||||
v-if="!isUnlinking"
|
||||
icon="unlink"
|
||||
size="12"
|
||||
type="outline"
|
||||
icon-lib="lucide"
|
||||
/>
|
||||
</woot-button>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
class="h-[24px]"
|
||||
color-scheme="secondary"
|
||||
/>
|
||||
<Button
|
||||
ghost
|
||||
xs
|
||||
slate
|
||||
class="!transition-none"
|
||||
icon="i-lucide-arrow-up-right"
|
||||
@click="openIssue"
|
||||
>
|
||||
<fluent-icon icon="arrow-up-right" size="14" />
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -8,6 +8,7 @@ import FilterButton from 'dashboard/components/ui/Dropdown/DropdownButton.vue';
|
||||
import FilterListDropdown from 'dashboard/components/ui/Dropdown/DropdownList.vue';
|
||||
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
conversationId: {
|
||||
@@ -106,9 +107,10 @@ const linkIssue = async () => {
|
||||
:class="shouldShowDropdown ? 'h-[256px]' : 'gap-2'"
|
||||
>
|
||||
<FilterButton
|
||||
right-icon="chevron-down"
|
||||
trailing-icon
|
||||
icon="i-lucide-chevron-down"
|
||||
:button-text="linkIssueTitle"
|
||||
class="justify-between w-full h-[2.5rem] py-1.5 px-3 rounded-xl border border-slate-50 bg-slate-25 dark:border-slate-600 dark:bg-slate-900 hover:bg-slate-50 dark:hover:bg-slate-900/50"
|
||||
class="justify-between w-full h-[2.5rem] py-1.5 px-3 rounded-xl bg-n-alpha-black2 outline outline-1 outline-n-weak dark:outline-n-weak hover:outline-n-slate-6 dark:hover:outline-n-slate-6"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<template v-if="shouldShowDropdown" #dropdown>
|
||||
@@ -129,20 +131,20 @@ const linkIssue = async () => {
|
||||
</template>
|
||||
</FilterButton>
|
||||
<div class="flex items-center justify-end w-full gap-2 mt-2">
|
||||
<woot-button
|
||||
class="px-4 rounded-xl button clear outline-woot-200/50 outline"
|
||||
<Button
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CANCEL')"
|
||||
@click.prevent="onClose"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.CANCEL') }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
:is-disabled="isSubmitDisabled"
|
||||
class="px-4 rounded-xl"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
:label="$t('INTEGRATION_SETTINGS.LINEAR.LINK.TITLE')"
|
||||
:disabled="isSubmitDisabled"
|
||||
:is-loading="isLinking"
|
||||
@click.prevent="linkIssue"
|
||||
>
|
||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.LINK.TITLE') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
+3
-2
@@ -50,9 +50,10 @@ const selectedItemId = computed(() => selectedItem.value?.id || null);
|
||||
<label class="w-full" :class="{ error: hasError }">
|
||||
{{ label }}
|
||||
<FilterButton
|
||||
right-icon="chevron-down"
|
||||
trailing-icon
|
||||
icon="i-lucide-chevron-down"
|
||||
:button-text="selectedItemName"
|
||||
class="justify-between w-full h-[2.5rem] py-1.5 px-3 rounded-xl border border-slate-50 bg-slate-25 dark:border-slate-600 dark:bg-slate-900 hover:bg-slate-50 dark:hover:bg-slate-900/50"
|
||||
class="justify-between w-full h-[2.5rem] py-1.5 px-3 rounded-xl bg-n-alpha-black2 outline outline-1 outline-n-weak dark:outline-n-weak hover:outline-n-slate-6 dark:hover:outline-n-slate-6"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<template v-if="shouldShowDropdown" #dropdown>
|
||||
|
||||
@@ -9,6 +9,7 @@ import Issue from './Issue.vue';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import { LINEAR_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import { parseLinearAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
const props = defineProps({
|
||||
conversationId: {
|
||||
@@ -100,23 +101,25 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="relative" :class="{ group: linkedIssue }">
|
||||
<woot-button
|
||||
<Button
|
||||
v-on-clickaway="closeIssue"
|
||||
v-tooltip="tooltipText"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
sm
|
||||
ghost
|
||||
slate
|
||||
class="!gap-1"
|
||||
@click="openIssue"
|
||||
>
|
||||
<fluent-icon
|
||||
icon="linear"
|
||||
size="19"
|
||||
class="text-[#5E6AD2]"
|
||||
class="text-[#5E6AD2] flex-shrink-0"
|
||||
view-box="0 0 19 19"
|
||||
/>
|
||||
<span v-if="linkedIssue" class="text-xs font-medium text-ash-800">
|
||||
<span v-if="linkedIssue" class="text-xs font-medium text-n-slate-11">
|
||||
{{ linkedIssue.issue.identifier }}
|
||||
</span>
|
||||
</woot-button>
|
||||
</Button>
|
||||
<Issue
|
||||
v-if="linkedIssue"
|
||||
:issue="linkedIssue.issue"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<script>
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
@@ -49,15 +54,13 @@ export default {
|
||||
:username="usernameAvatar"
|
||||
/>
|
||||
<div v-if="src && deleteAvatar" class="avatar-delete-btn">
|
||||
<woot-button
|
||||
color-scheme="alert"
|
||||
variant="hollow"
|
||||
size="tiny"
|
||||
type="button"
|
||||
<NextButton
|
||||
outline
|
||||
xs
|
||||
ruby
|
||||
:label="$t('INBOX_MGMT.DELETE.AVATAR_DELETE_BUTTON_TEXT')"
|
||||
@click="onAvatarDelete"
|
||||
>
|
||||
{{ $t('INBOX_MGMT.DELETE.AVATAR_DELETE_BUTTON_TEXT') }}
|
||||
</woot-button>
|
||||
/>
|
||||
</div>
|
||||
<label>
|
||||
<input
|
||||
|
||||
@@ -3,9 +3,12 @@ import { required } from '@vuelidate/validators';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import Modal from '../../Modal.vue';
|
||||
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
show: { type: Boolean, default: false },
|
||||
@@ -66,13 +69,20 @@ export default {
|
||||
:placeholder="confirmPlaceHolderText"
|
||||
@blur="v$.value.$touch"
|
||||
/>
|
||||
<div class="button-wrapper">
|
||||
<woot-button color-scheme="alert" :is-disabled="v$.value.$invalid">
|
||||
{{ confirmText }}
|
||||
</woot-button>
|
||||
<woot-button class="clear" @click.prevent="closeModal">
|
||||
{{ rejectText }}
|
||||
</woot-button>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<NextButton
|
||||
faded
|
||||
slate
|
||||
type="reset"
|
||||
:label="rejectText"
|
||||
@click.prevent="closeModal"
|
||||
/>
|
||||
<NextButton
|
||||
ruby
|
||||
type="submit"
|
||||
:label="confirmText"
|
||||
:disabled="v$.value.$invalid"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script>
|
||||
import Modal from '../../Modal.vue';
|
||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Modal,
|
||||
NextButton,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
@@ -55,12 +57,8 @@ export default {
|
||||
<div class="h-auto overflow-auto flex flex-col">
|
||||
<woot-modal-header :header-title="title" :header-content="description" />
|
||||
<div class="flex flex-row justify-end gap-2 py-4 px-6 w-full">
|
||||
<woot-button variant="clear" @click="cancel">
|
||||
{{ cancelLabel }}
|
||||
</woot-button>
|
||||
<woot-button @click="confirm">
|
||||
{{ confirmLabel }}
|
||||
</woot-button>
|
||||
<NextButton faded type="reset" :label="cancelLabel" @click="cancel" />
|
||||
<NextButton type="submit" :label="confirmLabel" @click="confirm" />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup>
|
||||
import Modal from '../../Modal.vue';
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
|
||||
defineProps({
|
||||
onClose: { type: Function, default: () => {} },
|
||||
@@ -22,23 +23,8 @@ const show = defineModel('show', { type: Boolean, default: false });
|
||||
:header-content-value="messageValue"
|
||||
/>
|
||||
<div class="flex items-center justify-end gap-2 p-8">
|
||||
<woot-button variant="clear" class="action-button" @click="onClose">
|
||||
{{ rejectText }}
|
||||
</woot-button>
|
||||
<woot-button
|
||||
color-scheme="alert"
|
||||
class="action-button"
|
||||
variant="smooth"
|
||||
@click="onConfirm"
|
||||
>
|
||||
{{ confirmText }}
|
||||
</woot-button>
|
||||
<Button faded slate type="reset" :label="rejectText" @click="onClose" />
|
||||
<Button ruby type="submit" :label="confirmText" @click="onConfirm" />
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.action-button {
|
||||
max-width: var(--space-giga);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,6 +21,7 @@ export const getAttributeInputType = (key, allCustomAttributes) => {
|
||||
const customAttribute = allCustomAttributes.find(
|
||||
attr => attr.attribute_key === key
|
||||
);
|
||||
|
||||
const { attribute_display_type } = customAttribute;
|
||||
const filterInputTypes = generateCustomAttributesInputType(
|
||||
attribute_display_type
|
||||
@@ -68,10 +69,22 @@ const getValuesForCountries = (values, countries) => {
|
||||
}));
|
||||
};
|
||||
|
||||
const getValuesForPriority = (values, priority) => {
|
||||
return priority.filter(option => values.includes(option.id));
|
||||
};
|
||||
|
||||
export const getValuesForFilter = (filter, params) => {
|
||||
const { attribute_key, values } = filter;
|
||||
const { languages, countries, agents, inboxes, teams, campaigns, labels } =
|
||||
params;
|
||||
const {
|
||||
languages,
|
||||
countries,
|
||||
agents,
|
||||
inboxes,
|
||||
teams,
|
||||
campaigns,
|
||||
labels,
|
||||
priority,
|
||||
} = params;
|
||||
switch (attribute_key) {
|
||||
case 'status':
|
||||
return getValuesForStatus(values);
|
||||
@@ -83,15 +96,14 @@ export const getValuesForFilter = (filter, params) => {
|
||||
return getValuesName(values, teams, 'id', 'name');
|
||||
case 'campaign_id':
|
||||
return getValuesName(values, campaigns, 'id', 'title');
|
||||
case 'labels': {
|
||||
case 'labels':
|
||||
return getValuesForLabels(values, labels);
|
||||
}
|
||||
case 'browser_language': {
|
||||
case 'priority':
|
||||
return getValuesForPriority(values, priority);
|
||||
case 'browser_language':
|
||||
return getValuesForLanguages(values, languages);
|
||||
}
|
||||
case 'country_code': {
|
||||
case 'country_code':
|
||||
return getValuesForCountries(values, countries);
|
||||
}
|
||||
default:
|
||||
return { id: values[0], name: values[0] };
|
||||
}
|
||||
@@ -100,9 +112,9 @@ export const getValuesForFilter = (filter, params) => {
|
||||
export const generateValuesForEditCustomViews = (filter, params) => {
|
||||
const { attribute_key, filter_operator, values } = filter;
|
||||
const { filterTypes, allCustomAttributes } = params;
|
||||
const inboxType = getInputType(attribute_key, filter_operator, filterTypes);
|
||||
const inputType = getInputType(attribute_key, filter_operator, filterTypes);
|
||||
|
||||
if (inboxType === undefined) {
|
||||
if (inputType === undefined) {
|
||||
const filterInputTypes = getAttributeInputType(
|
||||
attribute_key,
|
||||
allCustomAttributes
|
||||
@@ -112,7 +124,7 @@ export const generateValuesForEditCustomViews = (filter, params) => {
|
||||
: { id: values[0], name: values[0] };
|
||||
}
|
||||
|
||||
return inboxType === 'multi_select' || inboxType === 'search_select'
|
||||
return inputType === 'multi_select' || inputType === 'search_select'
|
||||
? getValuesForFilter(filter, params)
|
||||
: values[0].toString();
|
||||
};
|
||||
|
||||
@@ -295,7 +295,27 @@
|
||||
"CONVERSATION_INFO": "Conversation Information",
|
||||
"CONTACT_ATTRIBUTES": "Contact Attributes",
|
||||
"PREVIOUS_CONVERSATION": "Previous Conversations",
|
||||
"MACROS": "Macros"
|
||||
"MACROS": "Macros",
|
||||
"SHOPIFY_ORDERS": "Shopify Orders"
|
||||
},
|
||||
"SHOPIFY": {
|
||||
"ORDER_ID": "Order #{id}",
|
||||
"ERROR": "Error loading orders",
|
||||
"NO_SHOPIFY_ORDERS": "No orders found",
|
||||
"FINANCIAL_STATUS": {
|
||||
"PENDING": "Pending",
|
||||
"AUTHORIZED": "Authorized",
|
||||
"PARTIALLY_PAID": "Partially Paid",
|
||||
"PAID": "Paid",
|
||||
"PARTIALLY_REFUNDED": "Partially Refunded",
|
||||
"REFUNDED": "Refunded",
|
||||
"VOIDED": "Voided"
|
||||
},
|
||||
"FULFILLMENT_STATUS": {
|
||||
"FULFILLED": "Fulfilled",
|
||||
"PARTIALLY_FULFILLED": "Partially Fulfilled",
|
||||
"UNFULFILLED": "Unfulfilled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CONVERSATION_CUSTOM_ATTRIBUTES": {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"INTEGRATION_SETTINGS": {
|
||||
"SHOPIFY": {
|
||||
"DELETE": {
|
||||
"TITLE": "Delete Shopify Integration",
|
||||
"MESSAGE": "Are you sure you want to delete the Shopify integration?"
|
||||
},
|
||||
"STORE_URL": {
|
||||
"TITLE": "Connect Shopify Store",
|
||||
"LABEL": "Store URL",
|
||||
"PLACEHOLDER": "your-store.myshopify.com",
|
||||
"HELP": "Enter your Shopify store's myshopify.com URL",
|
||||
"CANCEL": "Cancel",
|
||||
"SUBMIT": "Connect Store"
|
||||
},
|
||||
"ERROR": "There was an error connecting to Shopify. Please try again or contact support if the issue persists."
|
||||
},
|
||||
"HEADER": "Integrations",
|
||||
"DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.",
|
||||
"LEARN_MORE": "Learn more about integrations",
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
"EMPTY_LIST": "No results found"
|
||||
},
|
||||
"PAGINATION": {
|
||||
"RESULTS": "Showing {start} to {end} of {total} results"
|
||||
"RESULTS": "Showing {start} to {end} of {total} results",
|
||||
"PER_PAGE_TEMPLATE": "{size} / page"
|
||||
}
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
|
||||
@@ -295,7 +295,27 @@
|
||||
"CONVERSATION_INFO": "معلومات المحادثة",
|
||||
"CONTACT_ATTRIBUTES": "سمات جهة الاتصال",
|
||||
"PREVIOUS_CONVERSATION": "المحادثات السابقة",
|
||||
"MACROS": "ماكروس"
|
||||
"MACROS": "ماكروس",
|
||||
"SHOPIFY_ORDERS": "Shopify Orders"
|
||||
},
|
||||
"SHOPIFY": {
|
||||
"ORDER_ID": "Order #{id}",
|
||||
"ERROR": "Error loading orders",
|
||||
"NO_SHOPIFY_ORDERS": "No orders found",
|
||||
"FINANCIAL_STATUS": {
|
||||
"PENDING": "معلق",
|
||||
"AUTHORIZED": "Authorized",
|
||||
"PARTIALLY_PAID": "Partially Paid",
|
||||
"PAID": "Paid",
|
||||
"PARTIALLY_REFUNDED": "Partially Refunded",
|
||||
"REFUNDED": "Refunded",
|
||||
"VOIDED": "Voided"
|
||||
},
|
||||
"FULFILLMENT_STATUS": {
|
||||
"FULFILLED": "Fulfilled",
|
||||
"PARTIALLY_FULFILLED": "Partially Fulfilled",
|
||||
"UNFULFILLED": "Unfulfilled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CONVERSATION_CUSTOM_ATTRIBUTES": {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"INTEGRATION_SETTINGS": {
|
||||
"SHOPIFY": {
|
||||
"DELETE": {
|
||||
"TITLE": "Delete Shopify Integration",
|
||||
"MESSAGE": "Are you sure you want to delete the Shopify integration?"
|
||||
},
|
||||
"STORE_URL": {
|
||||
"TITLE": "Connect Shopify Store",
|
||||
"LABEL": "Store URL",
|
||||
"PLACEHOLDER": "your-store.myshopify.com",
|
||||
"HELP": "Enter your Shopify store's myshopify.com URL",
|
||||
"CANCEL": "إلغاء",
|
||||
"SUBMIT": "Connect Store"
|
||||
},
|
||||
"ERROR": "There was an error connecting to Shopify. Please try again or contact support if the issue persists."
|
||||
},
|
||||
"HEADER": "خيارات الربط",
|
||||
"DESCRIPTION": "Chatwoot تتكامل مع أدوات وخدمات متعددة لتحسين كفاءة فريقك. استكشف القائمة أدناه لتكوين تطبيقاتك المفضلة.",
|
||||
"LEARN_MORE": "معرفة المزيد عن التكاملات",
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
"EMPTY_LIST": "لم يتم العثور على النتائج"
|
||||
},
|
||||
"PAGINATION": {
|
||||
"RESULTS": "Showing {start} to {end} of {total} results"
|
||||
"RESULTS": "Showing {start} to {end} of {total} results",
|
||||
"PER_PAGE_TEMPLATE": "{size} / page"
|
||||
}
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
|
||||
@@ -295,7 +295,27 @@
|
||||
"CONVERSATION_INFO": "Conversation Information",
|
||||
"CONTACT_ATTRIBUTES": "Contact Attributes",
|
||||
"PREVIOUS_CONVERSATION": "Previous Conversations",
|
||||
"MACROS": "Macros"
|
||||
"MACROS": "Macros",
|
||||
"SHOPIFY_ORDERS": "Shopify Orders"
|
||||
},
|
||||
"SHOPIFY": {
|
||||
"ORDER_ID": "Order #{id}",
|
||||
"ERROR": "Error loading orders",
|
||||
"NO_SHOPIFY_ORDERS": "No orders found",
|
||||
"FINANCIAL_STATUS": {
|
||||
"PENDING": "Pending",
|
||||
"AUTHORIZED": "Authorized",
|
||||
"PARTIALLY_PAID": "Partially Paid",
|
||||
"PAID": "Paid",
|
||||
"PARTIALLY_REFUNDED": "Partially Refunded",
|
||||
"REFUNDED": "Refunded",
|
||||
"VOIDED": "Voided"
|
||||
},
|
||||
"FULFILLMENT_STATUS": {
|
||||
"FULFILLED": "Fulfilled",
|
||||
"PARTIALLY_FULFILLED": "Partially Fulfilled",
|
||||
"UNFULFILLED": "Unfulfilled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CONVERSATION_CUSTOM_ATTRIBUTES": {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
{
|
||||
"INTEGRATION_SETTINGS": {
|
||||
"SHOPIFY": {
|
||||
"DELETE": {
|
||||
"TITLE": "Delete Shopify Integration",
|
||||
"MESSAGE": "Are you sure you want to delete the Shopify integration?"
|
||||
},
|
||||
"STORE_URL": {
|
||||
"TITLE": "Connect Shopify Store",
|
||||
"LABEL": "Store URL",
|
||||
"PLACEHOLDER": "your-store.myshopify.com",
|
||||
"HELP": "Enter your Shopify store's myshopify.com URL",
|
||||
"CANCEL": "Cancel",
|
||||
"SUBMIT": "Connect Store"
|
||||
},
|
||||
"ERROR": "There was an error connecting to Shopify. Please try again or contact support if the issue persists."
|
||||
},
|
||||
"HEADER": "Integrations",
|
||||
"DESCRIPTION": "Chatwoot integrates with multiple tools and services to improve your team's efficiency. Explore the list below to configure your favorite apps.",
|
||||
"LEARN_MORE": "Learn more about integrations",
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
"EMPTY_LIST": "No results found"
|
||||
},
|
||||
"PAGINATION": {
|
||||
"RESULTS": "Showing {start} to {end} of {total} results"
|
||||
"RESULTS": "Showing {start} to {end} of {total} results",
|
||||
"PER_PAGE_TEMPLATE": "{size} / page"
|
||||
}
|
||||
},
|
||||
"AGENT_REPORTS": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user