Merge branch 'develop' into chore/upgrade-page

This commit is contained in:
Sivin Varghese
2025-04-01 09:51:13 +05:30
committed by GitHub
85 changed files with 1407 additions and 1837 deletions
@@ -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,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';
@@ -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 />
@@ -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>
@@ -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>
@@ -41,7 +41,6 @@ describe('AccountSelector', () => {
'fluent-icon': FluentIcon,
},
stubs: {
WootButton: { template: '<button />' },
// override global stub
WootModalHeader: false,
},
@@ -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,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>
@@ -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>
@@ -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>
@@ -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>
@@ -1074,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')"
@@ -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>
@@ -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>
@@ -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>
@@ -107,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>
@@ -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>
@@ -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
@@ -387,7 +387,8 @@
"LABEL": "Company Name",
"PLACEHOLDER": "Wayne Enterprises"
},
"SUBMIT": "Submit"
"SUBMIT": "Submit",
"CANCEL": "Cancel"
}
},
"KEYBOARD_SHORTCUTS": {
@@ -1,123 +0,0 @@
<script>
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import SocialIcons from 'dashboard/routes/dashboard/conversation/contact/SocialIcons.vue';
export default {
components: {
Thumbnail,
SocialIcons,
},
props: {
contact: {
type: Object,
default: () => ({}),
},
},
emits: ['edit', 'message'],
computed: {
additionalAttributes() {
return this.contact.additional_attributes || {};
},
socialProfiles() {
const {
social_profiles: socialProfiles,
screen_name: twitterScreenName,
} = this.additionalAttributes;
return { twitter: twitterScreenName, ...(socialProfiles || {}) };
},
company() {
const { company = {} } = this.contact;
return company;
},
},
methods: {
onEditClick() {
this.$emit('edit');
},
onNewMessageClick() {
this.$emit('message');
},
},
};
</script>
<template>
<div class="contact--intro">
<Thumbnail
:src="contact.thumbnail"
size="64px"
:username="contact.name"
:status="contact.availability_status"
/>
<div class="contact--details">
<h2 class="text-lg contact--name">
{{ contact.name }}
</h2>
<h3 class="text-base contact--work">
{{ contact.title }}
<i v-if="company.name" class="i-lucide-circle-minus" />
<span class="company-name">{{ company.name }}</span>
</h3>
<p v-if="additionalAttributes.description" class="contact--bio">
{{ additionalAttributes.description }}
</p>
<SocialIcons :social-profiles="socialProfiles" />
</div>
<div class="contact-actions">
<woot-button
class="new-message"
size="small expanded"
icon="ion-paper-airplane"
@click="onNewMessageClick"
>
{{ $t('CONTACT_PANEL.NEW_MESSAGE') }}
</woot-button>
<woot-button
variant="hollow"
size="small expanded"
icon="edit"
@click="onEditClick"
>
{{ $t('EDIT_CONTACT.BUTTON_LABEL') }}
</woot-button>
</div>
</div>
</template>
<style scoped lang="scss">
.contact--details {
margin-top: var(--space-small);
}
.contact--work {
color: var(--color-body);
.icon {
font-size: var(--font-size-nano);
vertical-align: middle;
}
}
.contact--name {
text-transform: capitalize;
font-weight: var(--font-weight-bold);
}
.contact--bio {
margin: var(--space-smaller) 0 0;
}
.button.new-message {
margin-right: var(--space-small);
}
.contact-actions {
display: flex;
align-items: center;
width: 100%;
margin-top: var(--space-small);
}
</style>
@@ -13,12 +13,14 @@ import {
} from '../../../helper/AnalyticsHelper/events';
import MenuItem from '../../../components/widgets/conversation/contextMenu/menuItem.vue';
import { useTrack } from 'dashboard/composables';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
AddCannedModal,
MenuItem,
ContextMenu,
NextButton,
},
props: {
message: {
@@ -175,12 +177,12 @@ export default {
:confirm-text="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.DELETE')"
:reject-text="$t('CONVERSATION.CONTEXT_MENU.DELETE_CONFIRMATION.CANCEL')"
/>
<woot-button
<NextButton
v-if="!hideButton"
icon="more-vertical"
color-scheme="secondary"
variant="clear"
size="small"
ghost
slate
sm
icon="i-lucide-ellipsis-vertical"
class="invisible group-hover/context-menu:visible"
@click="handleOpen"
/>
@@ -1,7 +1,7 @@
<script>
import ConversationCard from 'dashboard/components/widgets/conversation/ConversationCard.vue';
import { mapGetters } from 'vuex';
import Spinner from 'shared/components/Spinner.vue';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
export default {
components: {
@@ -47,28 +47,25 @@ export default {
</script>
<template>
<div class="contact-conversation--panel">
<div v-if="!uiFlags.isFetching" class="contact-conversation__wrap">
<div
v-if="!previousConversations.length"
class="no-label-message px-4 p-3"
>
<span>
{{ $t('CONTACT_PANEL.CONVERSATIONS.NO_RECORDS_FOUND') }}
</span>
</div>
<div v-else class="contact-conversation--list">
<ConversationCard
v-for="conversation in previousConversations"
:key="conversation.id"
:chat="conversation"
:hide-inbox-name="false"
hide-thumbnail
class="compact"
/>
</div>
<div v-if="!uiFlags.isFetching" class="">
<div v-if="!previousConversations.length" class="no-label-message px-4 p-3">
<span>
{{ $t('CONTACT_PANEL.CONVERSATIONS.NO_RECORDS_FOUND') }}
</span>
</div>
<Spinner v-else />
<div v-else class="contact-conversation--list">
<ConversationCard
v-for="conversation in previousConversations"
:key="conversation.id"
:chat="conversation"
:hide-inbox-name="false"
hide-thumbnail
class="compact"
/>
</div>
</div>
<div v-else class="flex items-center justify-center py-5">
<Spinner />
</div>
</template>
@@ -79,6 +76,7 @@ export default {
::v-deep .conversation {
@apply pr-0;
.conversation--details {
@apply pl-2;
}
@@ -9,12 +9,14 @@ import ConversationLabels from './labels/LabelBox.vue';
import { CONVERSATION_PRIORITY } from '../../../../shared/constants/messages';
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
import { useTrack } from 'dashboard/composables';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
ContactDetailsItem,
MultiselectDropdown,
ConversationLabels,
NextButton,
},
props: {
conversationId: {
@@ -212,15 +214,15 @@ export default {
:title="$t('CONVERSATION_SIDEBAR.ASSIGNEE_LABEL')"
>
<template #button>
<woot-button
<NextButton
v-if="showSelfAssign"
icon="arrow-right"
variant="link"
size="small"
link
xs
icon="i-lucide-arrow-right"
class="!gap-1"
:label="$t('CONVERSATION_SIDEBAR.SELF_ASSIGN')"
@click="onSelfAssign"
>
{{ $t('CONVERSATION_SIDEBAR.SELF_ASSIGN') }}
</woot-button>
/>
</template>
</ContactDetailsItem>
<MultiselectDropdown
@@ -6,12 +6,14 @@ import { useAgentsList } from 'dashboard/composables/useAgentsList';
import ThumbnailGroup from 'dashboard/components/widgets/ThumbnailGroup.vue';
import MultiselectDropdownItems from 'shared/components/ui/MultiselectDropdownItems.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
Spinner,
ThumbnailGroup,
MultiselectDropdownItems,
NextButton,
},
props: {
conversationId: {
@@ -165,13 +167,14 @@ export default {
{{ $t('CONVERSATION_PARTICIPANTS.NO_PARTICIPANTS_TEXT') }}
</p>
</div>
<woot-button
<NextButton
v-tooltip.left="$t('CONVERSATION_PARTICIPANTS.ADD_PARTICIPANTS')"
slate
ghost
sm
icon="i-lucide-settings"
class="relative -top-1"
:title="$t('CONVERSATION_PARTICIPANTS.ADD_PARTICIPANTS')"
icon="settings"
size="tiny"
variant="smooth"
color-scheme="secondary"
@click="onOpenDropdown"
/>
</div>
@@ -188,15 +191,15 @@ export default {
>
{{ $t('CONVERSATION_PARTICIPANTS.YOU_ARE_WATCHING') }}
</p>
<woot-button
<NextButton
v-else
icon="arrow-right"
variant="link"
size="small"
link
xs
icon="i-lucide-arrow-right"
class="!gap-1"
:label="$t('CONVERSATION_PARTICIPANTS.WATCH_CONVERSATION')"
@click="onSelfAssign"
>
{{ $t('CONVERSATION_PARTICIPANTS.WATCH_CONVERSATION') }}
</woot-button>
/>
</div>
<div
v-on-clickaway="
@@ -213,13 +216,7 @@ export default {
>
{{ $t('CONVERSATION_PARTICIPANTS.ADD_PARTICIPANTS') }}
</h4>
<woot-button
icon="dismiss"
size="tiny"
color-scheme="secondary"
variant="clear"
@click="onCloseDropdown"
/>
<NextButton ghost slate xs icon="i-lucide-x" @click="onCloseDropdown" />
</div>
<MultiselectDropdownItems
:options="agentsList"
@@ -7,6 +7,7 @@ import { useUISettings } from 'dashboard/composables/useUISettings';
import Draggable from 'vuedraggable';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
import MacroItem from './MacroItem.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
defineProps({
conversationId: {
@@ -71,9 +72,13 @@ onMounted(() => {
{{ $t('MACROS.LIST.404') }}
</p>
<router-link :to="accountScopedUrl('settings/macros')">
<woot-button variant="smooth" icon="add" size="tiny" class="mt-1">
{{ $t('MACROS.HEADER_BTN_TXT') }}
</woot-button>
<NextButton
faded
xs
icon="i-lucide-plus"
class="mt-1"
:label="$t('MACROS.HEADER_BTN_TXT')"
/>
</router-link>
</div>
<div
@@ -53,9 +53,11 @@ const closeMacroPreview = () => {
<template>
<div
class="relative flex items-center justify-between leading-4 rounded-md button secondary clear"
class="relative flex items-center justify-between leading-4 rounded-md h-10 pl-3 pr-2"
>
<span class="overflow-hidden whitespace-nowrap text-ellipsis">
<span
class="overflow-hidden whitespace-nowrap text-ellipsis font-medium text-n-slate-12"
>
{{ macro.name }}
</span>
<div class="flex items-center gap-1 justify-end">
@@ -9,8 +9,12 @@ import { useVuelidate } from '@vuelidate/core';
import countries from 'shared/constants/countries.js';
import { isPhoneNumberValid } from 'shared/helpers/Validators';
import parsePhoneNumber from 'libphonenumber-js';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NextButton,
},
props: {
contact: {
type: Object,
@@ -401,16 +405,19 @@ export default {
/>
</div>
</div>
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
<div class="w-full">
<woot-submit-button
:loading="inProgress"
:button-text="$t('CONTACT_FORM.FORM.SUBMIT')"
/>
<button class="button clear" @click.prevent="onCancel">
{{ $t('CONTACT_FORM.FORM.CANCEL') }}
</button>
</div>
<div class="flex flex-row justify-start w-full gap-2 px-0 py-2">
<NextButton
type="submit"
:label="$t('CONTACT_FORM.FORM.SUBMIT')"
:is-loading="inProgress"
/>
<NextButton
faded
slate
type="reset"
:label="$t('CONTACT_FORM.FORM.CANCEL')"
@click.prevent="onCancel"
/>
</div>
</form>
</template>
@@ -2,10 +2,12 @@
import { useAlert } from 'dashboard/composables';
import EmojiOrIcon from 'shared/components/EmojiOrIcon.vue';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
EmojiOrIcon,
NextButton,
},
props: {
href: {
@@ -62,15 +64,13 @@ export default {
<span v-else class="text-sm text-n-slate-11">
{{ $t('CONTACT_PANEL.NOT_AVAILABLE') }}
</span>
<woot-button
<NextButton
v-if="showCopy"
type="submit"
variant="clear"
size="tiny"
color-scheme="secondary"
icon="clipboard"
class-names="p-0"
ghost
xs
slate
class="ltr:-ml-1 rtl:-mr-1"
icon="i-lucide-clipboard"
@click="onCopy"
/>
</a>
@@ -9,6 +9,7 @@ import { useI18n } from 'vue-i18n';
import { useUISettings } from 'dashboard/composables/useUISettings';
import { copyTextToClipboard } from 'shared/helpers/clipboard';
import CustomAttribute from 'dashboard/components/CustomAttribute.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
const props = defineProps({
attributeType: {
@@ -318,17 +319,16 @@ const evenClass = [
{{ emptyStateMessage }}
</p>
<!-- Show more and show less buttons show it if the combinedElements length is greater than 5 -->
<div v-if="combinedElements.length > 5" class="flex px-2 py-2">
<woot-button
size="small"
:icon="showAllAttributes ? 'chevron-up' : 'chevron-down'"
variant="clear"
color-scheme="primary"
class="!px-2 hover:!bg-transparent dark:hover:!bg-transparent"
<div v-if="combinedElements.length > 5" class="flex items-center px-2 py-2">
<NextButton
ghost
xs
:icon="
showAllAttributes ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'
"
:label="toggleButtonText"
@click="onClickToggle"
>
{{ toggleButtonText }}
</woot-button>
/>
</div>
</div>
</template>
@@ -2,6 +2,7 @@
import { mapGetters } from 'vuex';
import SwitchLayout from './SwitchLayout.vue';
import { frontendURL } from 'dashboard/helper/URLHelper';
export default {
components: {
SwitchLayout,
@@ -37,7 +38,7 @@ export default {
class="flex px-4 pb-1 justify-between items-center flex-row gap-1 pt-2.5 border-b border-transparent"
>
<woot-sidemenu-icon
size="tiny"
size="xs"
class="relative top-0 ltr:-ml-1.5 rtl:-mr-1.5 flex-shrink-0 focus:!bg-n-solid-3 dark:!hover:bg-n-solid-2 hover:!bg-n-alpha-2"
/>
<router-link
@@ -1,114 +0,0 @@
<script>
import { useVuelidate } from '@vuelidate/core';
import { required, minLength } from '@vuelidate/validators';
import { useAlert } from 'dashboard/composables';
import { CONTACTS_EVENTS } from '../../../helper/AnalyticsHelper/events';
import { useTrack } from 'dashboard/composables';
export default {
props: {
filterType: {
type: Number,
default: 0,
},
customViewsQuery: {
type: Object,
default: () => {},
},
openLastSavedItem: {
type: Function,
default: () => {},
},
},
emits: ['close'],
setup() {
return { v$: useVuelidate() };
},
data() {
return {
show: true,
name: '',
};
},
computed: {
isButtonDisabled() {
return this.v$.name.$invalid;
},
},
validations: {
name: {
required,
minLength: minLength(1),
},
},
methods: {
onClose() {
this.$emit('close');
},
async saveCustomViews() {
this.v$.$touch();
if (this.v$.$invalid) {
return;
}
try {
await this.$store.dispatch('customViews/create', {
name: this.name,
filter_type: this.filterType,
query: this.customViewsQuery,
});
this.alertMessage =
this.filterType === 0
? this.$t('FILTER.CUSTOM_VIEWS.ADD.API_FOLDERS.SUCCESS_MESSAGE')
: this.$t('FILTER.CUSTOM_VIEWS.ADD.API_SEGMENTS.SUCCESS_MESSAGE');
this.onClose();
useTrack(CONTACTS_EVENTS.SAVE_FILTER, {
type: this.filterType === 0 ? 'folder' : 'segment',
});
} catch (error) {
const errorMessage = error?.message;
this.alertMessage =
errorMessage || this.filterType === 0
? errorMessage
: this.$t('FILTER.CUSTOM_VIEWS.ADD.API_SEGMENTS.ERROR_MESSAGE');
} finally {
useAlert(this.alertMessage);
}
this.openLastSavedItem();
},
},
};
</script>
<template>
<woot-modal v-model:show="show" :on-close="onClose">
<woot-modal-header :header-title="$t('FILTER.CUSTOM_VIEWS.ADD.TITLE')" />
<form class="w-full" @submit.prevent="saveCustomViews">
<div class="w-full">
<woot-input
v-model="name"
:label="$t('FILTER.CUSTOM_VIEWS.ADD.LABEL')"
type="text"
:error="
v$.name.$error ? $t('FILTER.CUSTOM_VIEWS.ADD.ERROR_MESSAGE') : ''
"
:class="{ error: v$.name.$error }"
:placeholder="$t('FILTER.CUSTOM_VIEWS.ADD.PLACEHOLDER')"
@blur="v$.name.$touch"
/>
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
<woot-button :disabled="isButtonDisabled">
{{ $t('FILTER.CUSTOM_VIEWS.ADD.SAVE_BUTTON') }}
</woot-button>
<woot-button variant="clear" @click.prevent="onClose">
{{ $t('FILTER.CUSTOM_VIEWS.ADD.CANCEL_BUTTON') }}
</woot-button>
</div>
</div>
</form>
</woot-modal>
</template>
@@ -1,7 +1,12 @@
<script>
import { mapGetters } from 'vuex';
import wootConstants from 'dashboard/constants/globals';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NextButton,
},
data() {
return {
helpCenterDocsURL: wootConstants.HELP_CENTER_DOCS_URL,
@@ -86,21 +91,15 @@ export default {
v-if="isOnChatwootCloud"
class="flex flex-row gap-3 justify-start items-center sm:justify-center"
>
<woot-button
size="medium"
variant="hollow"
color-scheme="primary"
<NextButton
outline
:label="$t('HELP_CENTER.UPGRADE_PAGE.BUTTON.LEARN_MORE')"
@click="openHelpCenterDocs"
>
{{ $t('HELP_CENTER.UPGRADE_PAGE.BUTTON.LEARN_MORE') }}
</woot-button>
<woot-button
size="medium"
color-scheme="primary"
/>
<NextButton
:label="$t('HELP_CENTER.UPGRADE_PAGE.BUTTON.UPGRADE')"
@click="openBillingPage"
>
{{ $t('HELP_CENTER.UPGRADE_PAGE.BUTTON.UPGRADE') }}
</woot-button>
/>
</div>
</div>
<div
@@ -3,10 +3,12 @@ import { mapGetters } from 'vuex';
import NotificationPanelList from './NotificationPanelList.vue';
import { useTrack } from 'dashboard/composables';
import { ACCOUNT_EVENTS } from '../../../../helper/AnalyticsHelper/events';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NotificationPanelList,
NextButton,
},
emits: ['close'],
data() {
@@ -148,28 +150,27 @@ export default {
</span>
</div>
<div class="flex gap-2">
<woot-button
<NextButton
v-if="!noUnreadNotificationAvailable"
color-scheme="primary"
variant="smooth"
size="tiny"
faded
xs
icon="i-lucide-list-check"
:is-loading="uiFlags.isUpdating"
:label="$t('NOTIFICATIONS_PAGE.MARK_ALL_DONE')"
@click="onMarkAllDoneClick"
>
{{ $t('NOTIFICATIONS_PAGE.MARK_ALL_DONE') }}
</woot-button>
<woot-button
color-scheme="secondary"
variant="smooth"
size="tiny"
icon="settings"
/>
<NextButton
faded
xs
slate
icon="i-lucide-settings"
@click="openAudioNotificationSettings"
/>
<woot-button
color-scheme="secondary"
variant="link"
size="tiny"
icon="dismiss"
<NextButton
ghost
xs
slate
icon="i-lucide-x"
@click="closeNotificationPanel"
/>
</div>
@@ -186,25 +187,18 @@ export default {
class="flex items-center justify-between px-5 py-1"
>
<div class="flex">
<woot-button
size="medium"
variant="clear"
color-scheme="secondary"
:is-disabled="inFirstPage"
<NextButton
ghost
slate
icon="i-lucide-chevrons-left"
:disabled="inFirstPage"
@click="onClickFirstPage"
>
<fluent-icon icon="chevron-left" size="16" />
<fluent-icon
icon="chevron-left"
size="16"
class="rtl:-mr-3 ltr:-ml-3"
/>
</woot-button>
<woot-button
color-scheme="secondary"
variant="clear"
size="medium"
icon="chevron-left"
/>
<NextButton
ghost
slate
icon="i-lucide-chevron-left"
class="ltr:-ml-3 rtl:-mr-3"
:disabled="inFirstPage"
@click="onClickPreviousPage"
/>
@@ -213,28 +207,21 @@ export default {
{{ currentPage }} - {{ lastPage }}
</span>
<div class="flex">
<woot-button
color-scheme="secondary"
variant="clear"
size="medium"
icon="chevron-right"
<NextButton
ghost
slate
icon="i-lucide-chevron-right"
class="ltr:-mr-3 rtl:-ml-3"
:disabled="inLastPage"
@click="onClickNextPage"
/>
<woot-button
size="medium"
variant="clear"
color-scheme="secondary"
<NextButton
ghost
slate
icon="i-lucide-chevrons-right"
:disabled="inLastPage"
@click="onClickLastPage"
>
<fluent-icon icon="chevron-right" size="16" />
<fluent-icon
icon="chevron-right"
size="16"
class="rtl:-mr-3 ltr:-ml-3"
/>
</woot-button>
/>
</div>
</div>
<div v-else />
@@ -1,10 +1,12 @@
<script>
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import { dynamicTime } from 'shared/helpers/timeHelper';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
Thumbnail,
NextButton,
},
props: {
notificationItem: {
@@ -39,11 +41,10 @@ export default {
<template>
<div class="w-full">
<woot-button
size="expanded"
color-scheme="secondary"
variant="link"
class="w-full"
<NextButton
link
slate
class="!w-full !h-auto"
@click="onClickOpenNotification()"
>
<div
@@ -100,6 +101,6 @@ export default {
</span>
</div>
</div>
</woot-button>
</NextButton>
</div>
</template>
@@ -2,12 +2,14 @@
import Spinner from 'shared/components/Spinner.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
import NotificationPanelItem from './NotificationPanelItem.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NotificationPanelItem,
Spinner,
EmptyState,
NextButton,
},
props: {
notifications: {
@@ -59,16 +61,13 @@ export default {
v-if="showEmptyResult"
:title="$t('NOTIFICATIONS_PAGE.UNREAD_NOTIFICATION.EMPTY_MESSAGE')"
/>
<woot-button
<NextButton
v-if="!isLoading && inLastPage"
size="expanded"
variant="clear"
color-scheme="primary"
class-names="mt-3"
ghost
class="!w-full mt-3"
:label="$t('NOTIFICATIONS_PAGE.UNREAD_NOTIFICATION.ALL_NOTIFICATIONS')"
@click="openNotificationPage"
>
{{ $t('NOTIFICATIONS_PAGE.UNREAD_NOTIFICATION.ALL_NOTIFICATIONS') }}
</woot-button>
/>
<div
v-if="isLoading"
class="flex items-center justify-center mx-2 my-12 text-sm font-medium"
@@ -4,12 +4,14 @@ import Spinner from 'shared/components/Spinner.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
import { dynamicTime } from 'shared/helpers/timeHelper';
import { mapGetters } from 'vuex';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
Thumbnail,
Spinner,
EmptyState,
NextButton,
},
props: {
notifications: {
@@ -49,17 +51,22 @@ export default {
<template>
<section
class="flex-grow flex-shrink h-full px-4 py-8 overflow-hidden bg-white dark:bg-slate-900"
class="flex-grow flex-shrink h-full px-4 py-8 overflow-hidden bg-n-background"
>
<woot-submit-button
v-if="notificationMetadata.unreadCount"
class="button nice success button--fixed-top"
:button-text="$t('NOTIFICATIONS_PAGE.MARK_ALL_DONE')"
:loading="isUpdating"
@click="onMarkAllDoneClick"
/>
<table class="woot-table notifications-table">
<div class="flex w-full items-center justify-between gap-2 mb-4">
<h6 class="text-xl font-medium text-n-slate-12">
{{ $t('NOTIFICATIONS_PAGE.HEADER') }}
</h6>
<NextButton
v-if="notificationMetadata.unreadCount"
type="submit"
sm
:label="$t('NOTIFICATIONS_PAGE.MARK_ALL_DONE')"
:is-loading="isUpdating"
@click="onMarkAllDoneClick"
/>
</div>
<table class="woot-table notifications-table overflow-auto">
<tbody v-show="!isLoading">
<tr
v-for="notificationItem in notifications"
@@ -8,8 +8,8 @@ export const routes = [
path: frontendURL('accounts/:accountId/notifications'),
component: SettingsWrapper,
props: {
headerTitle: 'NOTIFICATIONS_PAGE.HEADER',
icon: 'alert',
headerTitle: '',
icon: '',
showNewButton: false,
showSidemenuIcon: false,
},
@@ -1,10 +1,12 @@
<script>
import { useAdmin } from 'dashboard/composables/useAdmin';
import BackButton from '../../../components/widgets/BackButton.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
BackButton,
NextButton,
},
props: {
headerTitle: {
@@ -70,15 +72,14 @@ export default {
{{ headerTitle }}
</span>
</h1>
<router-link
v-if="showNewButton && isAdmin"
:to="buttonRoute"
class="button success button--fixed-top rounded-[5px] flex gap-2"
>
<fluent-icon icon="add-circle" />
<span class="button__content">
{{ buttonText }}
</span>
<!-- TODO: Remove this when we are not using this -->
<router-link v-if="showNewButton && isAdmin" :to="buttonRoute">
<NextButton
teal
icon="i-lucide-circle-plus"
class="button--fixed-top"
:label="buttonText"
/>
</router-link>
</div>
</template>
@@ -4,10 +4,14 @@ import { useVuelidate } from '@vuelidate/core';
import { useAlert } from 'dashboard/composables';
import { required } from '@vuelidate/validators';
import router from '../../../../index';
import NextButton from 'dashboard/components-next/button/Button.vue';
import { isPhoneE164OrEmpty } from 'shared/helpers/Validators';
export default {
components: {
NextButton,
},
setup() {
return { v$: useVuelidate() };
},
@@ -115,9 +119,10 @@ export default {
</div>
<div class="w-full">
<woot-submit-button
:loading="uiFlags.isCreating"
:button-text="$t('INBOX_MGMT.ADD.WHATSAPP.SUBMIT_BUTTON')"
<NextButton
type="submit"
:label="$t('INBOX_MGMT.ADD.WHATSAPP.SUBMIT_BUTTON')"
:is-loading="uiFlags.isCreating"
/>
</div>
</form>
@@ -1,8 +1,12 @@
<script>
import { useAlert } from 'dashboard/composables';
import twitterClient from '../../../../../api/channel/twitterClient';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
NextButton,
},
data() {
return { isRequestingAuthorization: false };
},
@@ -31,11 +35,11 @@ export default {
>
<div class="login-init h-full text-center">
<form @submit.prevent="requestAuthorization">
<woot-submit-button
icon="brand-twitter"
button-text="Sign in with Twitter"
<NextButton
type="submit"
:loading="isRequestingAuthorization"
icon="i-ri-twitter-x-fill"
label="Sign in with Twitter"
:is-loading="isRequestingAuthorization"
/>
</form>
<p>{{ $t('INBOX_MGMT.ADD.TWITTER.HELP') }}</p>
@@ -51,7 +51,8 @@ const closeDropdown = () => emit('closeDropdown');
<template>
<FilterButton
right-icon="chevron-down"
trailing-icon
icon="i-lucide-chevron-down"
:button-text="name"
@click="toggleDropdown"
>
@@ -59,7 +59,11 @@ const closeDropdown = () => {
</script>
<template>
<FilterButton :button-text="name" left-icon="filter" @click="toggleDropdown">
<FilterButton
:button-text="name"
icon="i-lucide-filter"
@click="toggleDropdown"
>
<!-- Dropdown with search and sub-dropdown -->
<template v-if="showMenu" #dropdown>
<FilterListDropdown
@@ -1,8 +1,11 @@
<script>
import SLAPopoverCard from 'dashboard/components/widgets/conversation/components/SLAPopoverCard.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
SLAPopoverCard,
NextButton,
},
props: {
slaEvents: {
@@ -34,13 +37,13 @@ export default {
class="flex items-center col-span-2 text-slate-11 justify-end"
>
<div class="relative">
<woot-button
color-scheme="secondary"
variant="link"
<NextButton
link
slate
type="button"
:label="$t('SLA_REPORTS.TABLE.VIEW_DETAILS')"
@click="openSlaEvents"
>
{{ $t('SLA_REPORTS.TABLE.VIEW_DETAILS') }}
</woot-button>
/>
<SLAPopoverCard
v-if="showSlaPopoverCard"
:sla-missed-events="slaEvents"
@@ -1,5 +1,6 @@
<script setup>
import BaseEmptyState from './BaseEmptyState.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
const emit = defineEmits(['primaryAction']);
const primaryAction = () => emit('primaryAction');
@@ -10,13 +11,11 @@ const primaryAction = () => emit('primaryAction');
<p class="max-w-xs text-sm font-medium text-center">
{{ $t('SLA.LIST.404') }}
</p>
<woot-button
color-scheme="primary"
icon="plus-sign"
class="px-5 mt-4 rounded-xl"
<NextButton
icon="i-lucide-plus"
class="mt-4"
:label="$t('SLA.ADD_ACTION_LONG')"
@click="primaryAction"
>
{{ $t('SLA.ADD_ACTION_LONG') }}
</woot-button>
/>
</BaseEmptyState>
</template>
@@ -1,11 +1,11 @@
<script>
import emojis from './emojisGroup.json';
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
import WootButton from 'dashboard/components/ui/WootButton.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
const SEARCH_KEY = 'Search';
export default {
components: { FluentIcon, WootButton },
components: { FluentIcon, NextButton },
props: {
onClick: {
type: Function,
@@ -87,30 +87,31 @@ export default {
<template>
<div
role="dialog"
class="emoji-dialog bg-white shadow-lg dark:bg-slate-900 rounded-md border border-solid border-slate-75 dark:border-slate-800/50 box-content h-[18.75rem] absolute right-0 -top-[95px] w-80 z-20"
class="emoji-dialog bg-n-background shadow-lg rounded-md outline outline-1 outline-n-weak box-content h-[18.75rem] absolute right-0 -top-[95px] w-80 z-20"
>
<div class="flex flex-col">
<div class="flex gap-2 emoji-search--wrap">
<div class="flex gap-2 m-2 sticky top-2">
<input
ref="searchbar"
v-model="search"
type="text"
class="emoji-search--input focus:box-shadow-blue dark:focus:box-shadow-dark !mb-0 !h-8 !text-sm"
class="focus:box-shadow-blue dark:focus:box-shadow-dark !mb-0 !h-8 !text-sm"
:placeholder="$t('EMOJI.PLACEHOLDER')"
/>
<WootButton
<NextButton
v-if="showRemoveButton"
size="small"
variant="smooth"
class="dark:!bg-slate-800 dark:!hover:bg-slate-700"
color-scheme="secondary"
faded
sm
slate
class="flex-shrink-0"
:label="$t('EMOJI.REMOVE')"
@click="onClick('')"
>
{{ $t('EMOJI.REMOVE') }}
</WootButton>
/>
</div>
<div v-if="hasNoSearch" ref="emojiItem" class="emoji-item">
<h5 class="emoji-category--title">
<h5
class="text-sm text-n-slate-12 font-medium leading-normal m-0 py-1 px-2 capitalize"
>
{{ selectedKey }}
</h5>
<div class="emoji--row">
@@ -126,7 +127,10 @@ export default {
</div>
<div v-else ref="emojiItem" class="emoji-item">
<div v-for="category in filterAllEmojisBySearch" :key="category.slug">
<h5 v-if="category.emojis.length > 0" class="emoji-category--title">
<h5
v-if="category.emojis.length > 0"
class="text-sm text-n-slate-12 font-medium leading-normal m-0 py-1 px-2 capitalize"
>
{{ category.name }}
</h5>
<div v-if="category.emojis.length > 0" class="emoji--row">
@@ -140,29 +144,33 @@ export default {
/>
</div>
</div>
<div v-if="hasEmptySearchResult" class="empty-message">
<div class="emoji-icon">
<div
v-if="hasEmptySearchResult"
class="items-center flex flex-col h-[13.25rem] justify-center"
>
<div class="text-n-slate-11 mb-2">
<FluentIcon icon="emoji" size="48" />
</div>
<span class="empty-message--text">
<span class="text-n-slate-11 text-sm font-medium">
{{ $t('EMOJI.NOT_FOUND') }}
</span>
</div>
</div>
<div class="emoji-dialog--footer" role="menu">
<ul>
<div
class="emoji-dialog--footer relative w-full py-0 rounded-b-[0.34rem] px-1 bg-n-slate-3"
role="menu"
>
<ul
class="flex relative left-[2px] rtl:left-[unset] rtl:right-[2px] list-none m-0 overflow-auto py-1 px-0"
>
<li>
<button
class="emoji--item"
:class="{ active: selectedKey === 'Search' }"
@click="changeCategory('Search')"
>
<FluentIcon
icon="search"
size="16"
class="text-slate-700 dark:text-slate-100"
/>
<FluentIcon icon="search" size="16" class="text-n-slate-11" />
</button>
</li>
<li
@@ -223,7 +231,7 @@ export default {
}
.emoji--item {
@apply bg-transparent border-0 rounded cursor-pointer text-lg h-6 m-0 py-0 px-1 hover:bg-slate-75 dark:hover:bg-slate-800;
@apply bg-transparent border-0 rounded cursor-pointer text-lg h-6 m-0 py-0 px-1 hover:bg-n-slate-4;
}
.emoji--row {
@@ -234,53 +242,25 @@ export default {
}
}
.emoji-search--wrap {
@apply m-2 sticky top-2;
.emoji-search--input {
@apply text-sm focus-visible:border-transparent text-slate-800 dark:text-slate-100 h-8 m-0 p-2 w-full rounded-md bg-slate-50 dark:bg-slate-800 border border-solid border-transparent dark:border-slate-800/50;
}
}
.empty-message {
@apply items-center flex flex-col h-[13.25rem] justify-center;
.emoji-icon {
@apply text-slate-200 dark:text-slate-200 mb-2;
}
.empty-message--text {
@apply text-slate-200 dark:text-slate-200 text-sm font-medium;
}
}
.emoji-item {
@apply h-[13.25rem] overflow-y-auto;
}
.emoji-category--title {
@apply text-slate-800 text-sm dark:text-slate-100 font-medium leading-normal m-0 py-1 px-2 capitalize;
}
.emoji-dialog--footer {
@apply relative w-full py-0 rounded-b-[0.34rem] px-1 bg-slate-75 dark:bg-slate-800;
ul {
@apply flex relative left-[2px] rtl:left-[unset] rtl:right-[2px] list-none m-0 overflow-auto py-1 px-0;
> li {
@apply items-center cursor-pointer flex justify-center p-1;
}
li .active {
@apply bg-white dark:bg-slate-900;
@apply bg-n-background;
}
.emoji--item {
@apply items-center flex text-sm;
&:hover {
@apply bg-slate-75 dark:bg-slate-900;
@apply bg-n-slate-2;
}
}
}
@@ -4,12 +4,14 @@ import Hotkey from 'dashboard/components/base/Hotkey.vue';
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.vue';
import { picoSearch } from '@scmmishra/pico-search';
import { sanitizeLabel } from 'shared/helpers/sanitizeData';
import NextButton from 'dashboard/components-next/button/Button.vue';
export default {
components: {
LabelDropdownItem,
AddLabelModal,
Hotkey,
NextButton,
},
props: {
@@ -117,7 +119,7 @@ export default {
{{ $t('CONTACT_PANEL.LABELS.LABEL_SELECT.TITLE') }}
</h4>
<Hotkey
custom-class="border border-solid text-slate-800 dark:text-slate-100 bg-slate-50 dark:bg-slate-600 text-xxs border-slate-75 dark:border-slate-600"
custom-class="border border-solid text-slate-800 dark:text-slate-100 bg-slate-50 dark:bg-slate-600 text-xxs border-slate-75 dark:border-slate-600 flex-shrink-0"
>
{{ 'L' }}
</Hotkey>
@@ -156,19 +158,15 @@ export default {
v-if="allowCreation && shouldShowCreate"
class="flex pt-1 border-t border-solid border-slate-100 dark:border-slate-900"
>
<woot-button
size="small"
variant="clear"
color-scheme="secondary"
icon="add"
is-expanded
class="button-new-label"
:is-disabled="hasExactMatchInResults"
<NextButton
icon="i-lucide-plus"
slate
sm
ghost
:label="`${createLabelPlaceholder} ${parsedSearch}`"
:disabled="hasExactMatchInResults"
@click="showCreateModal"
>
{{ createLabelPlaceholder }}
{{ parsedSearch }}
</woot-button>
/>
<woot-modal
v-model:show="createModalVisible"
@@ -184,21 +182,3 @@ export default {
</div>
</div>
</template>
<style lang="scss" scoped>
.hotkey {
@apply flex-shrink-0;
}
.search-input {
@apply m-0 w-full border border-solid border-transparent h-8 text-sm text-slate-700 dark:text-slate-100 rounded-md focus:border-woot-500 bg-slate-50 dark:bg-slate-900;
}
.button-new-label {
@apply whitespace-nowrap text-ellipsis overflow-hidden items-center;
.icon {
@apply min-w-0;
}
}
</style>
@@ -6,7 +6,7 @@ import { nextTick } from 'vue';
const config = {
global: {
stubs: {
WootButton: { template: '<button />' },
NextButton: { template: '<button />' },
WootInput: { template: '<input />' },
},
},