Merge branch 'develop' into report-rewrite

This commit is contained in:
Pranav
2024-03-28 16:00:01 -07:00
1120 changed files with 20061 additions and 6509 deletions
+6 -2
View File
@@ -8,8 +8,8 @@
>
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
<template v-if="currentAccountId">
<pending-email-verification-banner />
<payment-pending-banner />
<pending-email-verification-banner v-if="hideOnOnboardingView" />
<payment-pending-banner v-if="hideOnOnboardingView" />
<upgrade-banner />
</template>
<transition name="fade" mode="out-in">
@@ -38,6 +38,7 @@ import vueActionCable from './helper/actionCable';
import WootSnackbarBox from './components/SnackbarContainer.vue';
import rtlMixin from 'shared/mixins/rtlMixin';
import { setColorTheme } from './helper/themeHelper';
import { isOnOnboardingView } from 'v3/helpers/RouteHelper';
import {
registerSubscription,
verifyServiceWorkerExistence,
@@ -79,6 +80,9 @@ export default {
const { accounts = [] } = this.currentUser || {};
return accounts.length > 0;
},
hideOnOnboardingView() {
return !isOnOnboardingView(this.$route);
},
},
watch: {
+8
View File
@@ -1,9 +1,17 @@
/* global axios */
import ApiClient from './ApiClient';
class Agents extends ApiClient {
constructor() {
super('agents', { accountScoped: true });
}
bulkInvite({ emails }) {
return axios.post(`${this.url}/bulk_create`, {
emails,
});
}
}
export default new Agents();
@@ -7,12 +7,13 @@ class NotificationsAPI extends ApiClient {
}
get({ page, status, type, sortOrder }) {
const includesFilter = [status, type].filter(value => !!value);
return axios.get(this.url, {
params: {
page,
status,
type,
sort_order: sortOrder,
includes: includesFilter,
},
});
}
+18
View File
@@ -84,6 +84,24 @@ class ReportsAPI extends ApiClient {
params: { since, until, business_hours: businessHours },
});
}
getBotMetrics({ from, to } = {}) {
return axios.get(`${this.url}/bot_metrics`, {
params: { since: from, until: to },
});
}
getBotSummary({ from, to, groupBy, businessHours } = {}) {
return axios.get(`${this.url}/bot_summary`, {
params: {
since: from,
until: to,
type: 'account',
group_by: groupBy,
business_hours: businessHours,
},
});
}
}
export default new ReportsAPI();
+9
View File
@@ -0,0 +1,9 @@
import ApiClient from './ApiClient';
class SlaAPI extends ApiClient {
constructor() {
super('sla_policies', { accountScoped: true });
}
}
export default new SlaAPI();
@@ -10,4 +10,29 @@ describe('#AgentAPI', () => {
expect(agents).toHaveProperty('update');
expect(agents).toHaveProperty('delete');
});
describe('API calls', () => {
const originalAxios = window.axios;
const axiosMock = {
post: jest.fn(() => Promise.resolve()),
};
beforeEach(() => {
window.axios = axiosMock;
});
afterEach(() => {
window.axios = originalAxios;
});
it('#bulkInvite', () => {
agents.bulkInvite({ emails: ['hello@hi.com'] });
expect(axiosMock.post).toHaveBeenCalledWith(
'/api/v1/agents/bulk_create',
{
emails: ['hello@hi.com'],
}
);
});
});
});
@@ -27,20 +27,36 @@ describe('#NotificationAPI', () => {
window.axios = originalAxios;
});
it('#get', () => {
notificationsAPI.get({
page: 1,
status: 'read',
type: 'Conversation',
sortOrder: 'desc',
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {
describe('#get', () => {
it('generates the API call if both params are available', () => {
notificationsAPI.get({
page: 1,
status: 'read',
type: 'Conversation',
sort_order: 'desc',
},
status: 'snoozed',
type: 'read',
sortOrder: 'desc',
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {
page: 1,
sort_order: 'desc',
includes: ['snoozed', 'read'],
},
});
});
it('generates the API call if one of the params are available', () => {
notificationsAPI.get({
page: 1,
type: 'read',
sortOrder: 'desc',
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {
page: 1,
sort_order: 'desc',
includes: ['read'],
},
});
});
});
@@ -111,6 +111,40 @@ describe('#Reports API', () => {
});
});
it('#getBotMetrics', () => {
reportsAPI.getBotMetrics({ from: 1621103400, to: 1621621800 });
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v2/reports/bot_metrics',
{
params: {
since: 1621103400,
until: 1621621800,
},
}
);
});
it('#getBotSummary', () => {
reportsAPI.getBotSummary({
from: 1621103400,
to: 1621621800,
groupBy: 'date',
businessHours: true,
});
expect(axiosMock.get).toHaveBeenCalledWith(
'/api/v2/reports/bot_summary',
{
params: {
since: 1621103400,
until: 1621621800,
type: 'account',
group_by: 'date',
business_hours: true,
},
}
);
});
it('#getConversationMetric', () => {
reportsAPI.getConversationMetric('account');
expect(axiosMock.get).toHaveBeenCalledWith(
@@ -1,58 +0,0 @@
.button {
font-family: $body-font-family;
font-weight: $font-weight-medium;
&.round {
border-radius: 1000px;
}
}
select {
height: 2.5rem;
}
.card {
margin-bottom: var(--space-small);
padding: var(--space-normal);
}
code {
border: 0;
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
'"Liberation Mono"', '"Courier New"', 'monospace';
font-size: $font-size-mini;
&.hljs {
background: $color-background;
border-radius: var(--border-radius-large);
padding: $space-two;
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100;
}
}
.text-truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-capitalize {
text-transform: capitalize;
}
.cursor-pointer {
cursor: pointer;
}
// remove when grid gutters are fixed
.columns.with-right-space {
padding-right: var(--space-normal);
}
.badge {
border-radius: var(--border-radius-normal);
}
.padding-right-small {
padding-right: var(--space-one);
}
@@ -1,623 +0,0 @@
// Foundation for Sites Settings
// -----------------------------
//
// Table of Contents:
//
// 1. Global
// 2. Breakpoints
// 3. The Grid
// 4. Base Typography
// 5. Typography Helpers
// 6. Abide
// 7. Accordion
// 8. Accordion Menu
// 9. Badge
// 10. Breadcrumbs
// 11. Button
// 12. Button Group
// 13. Callout
// 14. Card
// 15. Close Button
// 16. Drilldown
// 17. Dropdown
// 18. Dropdown Menu
// 19. Forms
// 20. Label
// 21. Media Object
// 22. Menu
// 23. Meter
// 24. Off-canvas
// 25. Orbit
// 26. Pagination
// 27. Progress Bar
// 28. Responsive Embed
// 29. Reveal
// 30. Slider
// 31. Switch
// 32. Table
// 33. Tabs
// 34. Thumbnail
// 35. Title Bar
// 36. Tooltip
// 37. Top Bar
@import '~foundation-sites/scss/util/util';
// 1. Global
// ---------
// Disable contrast warnings in Foundation.
$contrast-warnings: false;
$global-font-size: 16px;
$global-width: 100%;
$global-lineheight: 1.5;
$foundation-palette: (primary: $color-woot,
secondary: #5d7592,
success: #44ce4b,
warning: #ffc532,
alert: #ff382d);
$light-gray: #c0ccda;
$medium-gray: #8492a6;
$dark-gray: $color-gray;
$black: #000;
$white: #fff;
$body-background: $white;
$body-font-color: $color-body;
$body-font-family: 'PlusJakarta',
-apple-system,
system-ui,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Tahoma,
Arial,
sans-serif;
$body-antialiased: true;
$global-margin: $space-small;
$global-padding: $space-small;
$global-weight-normal: normal;
$global-weight-bold: bold;
$global-radius: 0;
$global-text-direction: ltr;
$global-flexbox: false;
$print-transparent-backgrounds: true;
@include add-foundation-colors;
// 2. Breakpoints
// --------------
$breakpoints: (small: 0,
medium: 640px,
large: 1024px,
xlarge: 1200px,
xxlarge: 1400px,
xxxlarge: 1600px,
);
$print-breakpoint: large;
$breakpoint-classes: (small medium large);
// 3. The Grid
// -----------
$grid-row-width: $global-width;
$grid-column-count: 12;
$grid-column-gutter: (small: $zero,
medium: $zero);
$grid-column-align-edge: true;
$block-grid-max: 8;
// 4. Base Typography
// ------------------
$header-font-family: $body-font-family;
$header-font-weight: $font-weight-medium;
$header-font-style: normal;
$font-family-monospace: $body-font-family;
$header-color: $color-heading;
$header-lineheight: 1.4;
$header-margin-bottom: 0.3125rem;
$header-styles: (small: ("h1": ("font-size": 24),
"h2": ("font-size": 20),
"h3": ("font-size": 19),
"h4": ("font-size": 18),
"h5": ("font-size": 17),
"h6": ("font-size": 16)),
medium: ("h1": ("font-size": 48),
"h2": ("font-size": 40),
"h3": ("font-size": 31),
"h4": ("font-size": 25),
"h5": ("font-size": 20),
"h6": ("font-size": 16)));
$header-text-rendering: optimizeLegibility;
$small-font-size: 80%;
$header-small-font-color: $medium-gray;
$paragraph-lineheight: 1.65;
$paragraph-margin-bottom: var(--space-small);
$paragraph-text-rendering: optimizeLegibility;
$code-color: $black;
$code-font-family: $font-family-monospace;
$code-font-weight: $global-weight-normal;
$code-background: $light-gray;
$code-border: 1px solid $medium-gray;
$code-padding: rem-calc(2 5 1);
$anchor-color: $primary-color;
$anchor-color-hover: scale-color($anchor-color, $lightness: -14%);
$anchor-text-decoration: none;
$anchor-text-decoration-hover: none;
$hr-width: $global-width;
$hr-border: 1px solid $medium-gray;
$hr-margin: rem-calc(20) auto;
$list-lineheight: $paragraph-lineheight;
$list-margin-bottom: $paragraph-margin-bottom;
$list-style-type: disc;
$list-style-position: outside;
$list-side-margin: 0.78125rem;
$list-nested-side-margin: 0.78125rem;
$defnlist-margin-bottom: 0.6875rem;
$defnlist-term-weight: $global-weight-bold;
$defnlist-term-margin-bottom: 0.1875rem;
$blockquote-color: $dark-gray;
$blockquote-padding: rem-calc(9 20 0 19);
$blockquote-border: 1px solid $medium-gray;
$cite-font-size: rem-calc(13);
$cite-color: $dark-gray;
$cite-pseudo-content: '\2014 \0020';
$keystroke-font: $font-family-monospace;
$keystroke-color: $black;
$keystroke-background: $light-gray;
$keystroke-padding: rem-calc(2 4 0);
$keystroke-radius: $global-radius;
$abbr-underline: 1px dotted $black;
// 5. Typography Helpers
// ---------------------
$lead-font-size: $global-font-size * 1.25;
$lead-lineheight: 1.6;
$subheader-lineheight: 1.4;
$subheader-color: $dark-gray;
$subheader-font-weight: $global-weight-normal;
$subheader-margin-top: 0.125rem;
$subheader-margin-bottom: 0.3125rem;
$stat-font-size: 1.5625rem;
// 6. Abide
// --------
$abide-inputs: true;
$abide-labels: true;
$input-background-invalid: get-color(alert);
$form-label-color-invalid: get-color(alert);
$input-error-color: get-color(alert);
$input-error-font-size: rem-calc(12);
$input-error-font-weight: $global-weight-bold;
// 7. Accordion
// ------------
$accordion-background: $white;
$accordion-plusminus: true;
$accordion-title-font-size: rem-calc(12);
$accordion-item-color: $primary-color;
$accordion-item-background-hover: $light-gray;
$accordion-item-padding: 0.78125rem 0.625rem;
$accordion-content-background: $white;
$accordion-content-border: 1px solid $light-gray;
$accordion-content-color: $body-font-color;
$accordion-content-padding: 0.625rem;
// 8. Accordion Menu
// -----------------
$accordionmenu-arrows: true;
$accordionmenu-arrow-color: $primary-color;
$accordionmenu-arrow-size: 6px;
// 9. Badge
// --------
$badge-background: $primary-color;
$badge-color: $white;
$badge-color-alt: $black;
$badge-palette: $foundation-palette;
$badge-padding: var(--space-smaller);
$badge-minwidth: 2.1em;
$badge-font-size: var(--font-size-nano);
// 10. Breadcrumbs
// ---------------
$breadcrumbs-margin: 0 0 $global-margin 0;
$breadcrumbs-item-font-size: rem-calc(11);
$breadcrumbs-item-color: $primary-color;
$breadcrumbs-item-color-current: $black;
$breadcrumbs-item-color-disabled: $medium-gray;
$breadcrumbs-item-margin: 0.46875rem;
$breadcrumbs-item-uppercase: true;
$breadcrumbs-item-slash: true;
// 11. Button
// ----------
$button-padding: var(--space-smaller) 1em;
$button-margin: 0 0 $global-margin 0;
$button-fill: solid;
$button-background: $primary-color;
$button-background-hover: scale-color($button-background, $lightness: -15%);
$button-color: $white;
$button-color-alt: $white;
$button-radius: var(--border-radius-normal);
$button-sizes: (tiny: var(--font-size-micro),
small: var(--font-size-mini),
default: var(--font-size-small),
large: var(--font-size-medium));
$button-palette: $foundation-palette;
$button-opacity-disabled: 0.4;
$button-background-hover-lightness: -20%;
$button-hollow-hover-lightness: -50%;
$button-transition: background-color 0.25s ease-out,
color 0.25s ease-out;
// 12. Button Group
// ----------------
$buttongroup-margin: 0;
$buttongroup-spacing: 0;
$buttongroup-child-selector: '.button';
$buttongroup-expand-max: 6;
$buttongroup-radius-on-each: false;
// 13. Callout
// -----------
$callout-background: $white;
$callout-background-fade: 85%;
$callout-border: 1px solid rgba($black, 0.25);
$callout-margin: 0 0 0.625rem 0;
$callout-padding: 0.625rem;
$callout-font-color: $body-font-color;
$callout-font-color-alt: $body-background;
$callout-radius: $global-radius;
$callout-link-tint: 30%;
// 14. Card
// --------
$card-background: $white;
$card-font-color: $body-font-color;
$card-divider-background: $light-gray;
$card-border: 1px solid var(--color-border);
$card-shadow: var(--shadow-small);
$card-border-radius: var(--border-radius-normal);
$card-padding: var(--space-small);
$card-margin: $global-margin;
// 15. Close Button
// ----------------
$closebutton-position: right top;
$closebutton-offset-horizontal: (small: 0.66rem,
medium: 1rem);
$closebutton-offset-vertical: (small: 0.33em,
medium: 0.5rem);
$closebutton-size: (small: 1.5em,
medium: 2em);
$closebutton-lineheight: 1;
$closebutton-color: $dark-gray;
$closebutton-color-hover: $black;
// 16. Drilldown
// -------------
$drilldown-transition: transform 0.15s linear;
$drilldown-arrows: true;
$drilldown-arrow-color: $primary-color;
$drilldown-arrow-size: 6px;
$drilldown-background: $white;
// 17. Dropdown
// ------------
$dropdown-padding: 0.625rem;
$dropdown-background: $body-background;
$dropdown-border: 1px solid $medium-gray;
$dropdown-font-size: 0.625rem;
$dropdown-width: 300px;
$dropdown-radius: $global-radius;
$dropdown-sizes: (tiny: 100px,
small: 200px,
large: 400px);
// 18. Dropdown Menu
// -----------------
$dropdownmenu-arrows: true;
$dropdownmenu-arrow-color: $anchor-color;
$dropdownmenu-arrow-size: 6px;
$dropdownmenu-min-width: 200px;
$dropdownmenu-background: $white;
$dropdownmenu-border: 1px solid $medium-gray;
// 19. Forms
// ---------
$fieldset-border: 1px solid $light-gray;
$fieldset-padding: $space-two;
$fieldset-margin: $space-one $zero;
$legend-padding: rem-calc(0 3);
$form-spacing: $space-normal;
$helptext-color: $color-body;
$helptext-font-size: $font-size-small;
$helptext-font-style: italic;
$input-prefix-color: $color-body;
$input-prefix-background: var(--b-100);
$input-prefix-border: 1px solid $color-border;
$input-prefix-padding: 0.625rem;
$form-label-color: $color-body;
$form-label-font-size: rem-calc(14);
$form-label-font-weight: $font-weight-medium;
$form-label-line-height: 1.8;
$select-background: $white;
$select-triangle-color: $dark-gray;
$select-radius: var(--border-radius-normal);
$input-color: $color-body;
$input-placeholder-color: $light-gray;
$input-font-family: inherit;
$input-font-size: $font-size-default;
$input-font-weight: $global-weight-normal;
$input-background: $white;
$input-background-focus: $white;
$input-background-disabled: $light-gray;
$input-border: 1px solid var(--s-200);
$input-border-focus: 1px solid lighten($primary-color, 15%);
$input-shadow: 0;
$input-shadow-focus: 0;
$input-cursor-disabled: not-allowed;
$input-transition: border-color 0.25s ease-in-out;
$input-number-spinners: true;
$input-radius: var(--border-radius-normal);
$form-button-radius: var(--border-radius-normal);
// 20. Label
// ---------
$label-background: $white;
$label-color: $black;
$label-color-alt: $black;
$label-palette: $foundation-palette;
$label-font-size: $font-size-mini;
$label-padding: $space-smaller $space-small;
$label-radius: var(--border-radius-small);
// 21. Media Object
// ----------------
$mediaobject-margin-bottom: $global-margin;
$mediaobject-section-padding: $global-padding;
$mediaobject-image-width-stacked: 100%;
// 22. Menu
// --------
$menu-margin: 0;
$menu-margin-nested: $space-medium;
$menu-item-padding: $space-slab;
$menu-item-color-active: $white;
$menu-item-background-active: $color-background;
$menu-icon-spacing: 0.15625rem;
$menu-item-background-hover: $light-gray;
$menu-border: $light-gray;
// 23. Meter
// ---------
$meter-height: 0.625rem;
$meter-radius: $global-radius;
$meter-background: $medium-gray;
$meter-fill-good: $success-color;
$meter-fill-medium: $warning-color;
$meter-fill-bad: $alert-color;
// 24. Off-canvas
// --------------
$offcanvas-sizes: (small: 14.375,
medium: 14.375,
);
$offcanvas-vertical-sizes: (small: 14.375,
medium: 14.375,
);
$offcanvas-background: $light-gray;
$offcanvas-shadow: 0 0 10px rgba($black, 0.7);
$offcanvas-push-zindex: 1;
$offcanvas-overlap-zindex: 10;
$offcanvas-reveal-zindex: 1;
$offcanvas-transition-length: 0.5s;
$offcanvas-transition-timing: ease;
$offcanvas-fixed-reveal: true;
$offcanvas-exit-background: rgba($white, 0.25);
$maincontent-class: 'off-canvas-content';
// 25. Orbit
// ---------
$orbit-bullet-background: $medium-gray;
$orbit-bullet-background-active: $dark-gray;
$orbit-bullet-diameter: 0.75rem;
$orbit-bullet-margin: 0.0625rem;
$orbit-bullet-margin-top: 0.5rem;
$orbit-bullet-margin-bottom: 0.5rem;
$orbit-caption-background: rgba($black, 0.5);
$orbit-caption-padding: 0.625rem;
$orbit-control-background-hover: rgba($black, 0.5);
$orbit-control-padding: 0.625rem;
$orbit-control-zindex: 10;
// 26. Pagination
// --------------
$pagination-font-size: rem-calc(14);
$pagination-margin-bottom: $global-margin;
$pagination-item-color: $black;
$pagination-item-padding: rem-calc(3 10);
$pagination-item-spacing: rem-calc(1);
$pagination-radius: $global-radius;
$pagination-item-background-hover: $light-gray;
$pagination-item-background-current: $primary-color;
$pagination-item-color-current: $white;
$pagination-item-color-disabled: $medium-gray;
$pagination-ellipsis-color: $black;
$pagination-mobile-items: false;
$pagination-mobile-current-item: false;
$pagination-arrows: true;
// 27. Progress Bar
// ----------------
$progress-height: 0.625rem;
$progress-background: $medium-gray;
$progress-margin-bottom: $global-margin;
$progress-meter-background: $primary-color;
$progress-radius: $global-radius;
// 28. Responsive Embed
// --------------------
$responsive-embed-margin-bottom: rem-calc(16);
$responsive-embed-ratios: (default: 4 by 3,
widescreen: 16 by 9);
// 29. Reveal
// ----------
$reveal-background: $white;
$reveal-width: 600px;
$reveal-max-width: $global-width;
$reveal-padding: $global-padding;
$reveal-border: 1px solid $medium-gray;
$reveal-radius: $global-radius;
$reveal-zindex: 1005;
$reveal-overlay-background: rgba($black, 0.45);
// 30. Slider
// ----------
$slider-width-vertical: 0.3125rem;
$slider-transition: all 0.2s ease-in-out;
$slider-height: 0.3125rem;
$slider-background: $light-gray;
$slider-fill-background: $medium-gray;
$slider-handle-height: 0.875rem;
$slider-handle-width: 0.875rem;
$slider-handle-background: $primary-color;
$slider-opacity-disabled: 0.25;
$slider-radius: $global-radius;
// 31. Switch
// ----------
$switch-background: $light-gray;
$switch-background-active: $primary-color;
$switch-height: $space-two;
$switch-height-tiny: $space-slab;
$switch-height-small: $space-normal;
$switch-height-large: $space-large;
$switch-radius: $space-large;
$switch-margin: $global-margin;
$switch-paddle-background: $white;
$switch-paddle-offset: $space-micro;
$switch-paddle-radius: $space-large;
$switch-paddle-transition: all 0.15s ease-out;
// 32. Table
// ---------
$table-background: transparent;
$table-color-scale: 5%;
$table-border: 1px solid transparent;
$table-padding: rem-calc(8 10 10);
$table-hover-scale: 2%;
$table-row-hover: darken($table-background, $table-hover-scale);
$table-row-stripe-hover: darken($table-background,
$table-color-scale + $table-hover-scale);
$table-is-striped: false;
$table-striped-background: smart-scale($table-background, $table-color-scale);
$table-stripe: even;
$table-head-background: smart-scale($table-background, $table-color-scale / 2);
$table-head-row-hover: darken($table-head-background, $table-hover-scale);
$table-foot-background: smart-scale($table-background, $table-color-scale);
$table-foot-row-hover: darken($table-foot-background, $table-hover-scale);
$table-head-font-color: $body-font-color;
$table-foot-font-color: $body-font-color;
$show-header-for-stacked: false;
// 33. Tabs
// --------
$tab-margin: 0;
$tab-background: transparent;
$tab-background-active: transparent;
$tab-item-font-size: $font-size-small;
$tab-item-background-hover: transparent;
$tab-item-padding: $space-one $zero;
$tab-color: $primary-color;
$tab-active-color: $primary-color;
$tab-expand-max: 6;
$tab-content-background: transparent;
$tab-content-border: transparent;
$tab-content-color: foreground($tab-background, $primary-color);
$tab-content-padding: 0.625rem;
// 34. Thumbnail
// -------------
$thumbnail-border: solid 4px $white;
$thumbnail-margin-bottom: $global-margin;
$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2);
$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5);
$thumbnail-transition: box-shadow 200ms ease-out;
$thumbnail-radius: $global-radius;
// 35. Title Bar
// -------------
$titlebar-background: $black;
$titlebar-color: $white;
$titlebar-padding: 0.3125rem;
$titlebar-text-font-weight: bold;
$titlebar-icon-color: $white;
$titlebar-icon-color-hover: $medium-gray;
$titlebar-icon-spacing: 0.15625rem;
// 36. Tooltip
// -----------
$has-tip-font-weight: $global-weight-bold;
$has-tip-border-bottom: dotted 1px $dark-gray;
$tooltip-background-color: $black;
$tooltip-color: $white;
$tooltip-padding: 0.46875rem;
$tooltip-font-size: $font-size-mini;
$tooltip-pip-width: 0.46875rem;
$tooltip-pip-height: $tooltip-pip-width * 0.866;
$tooltip-radius: $global-radius;
// 37. Top Bar
// -----------
$topbar-padding: 0.3125;
$topbar-background: $light-gray;
$topbar-submenu-background: $topbar-background;
$topbar-title-spacing: 0.3125 0.625rem 0.3125 0;
$topbar-input-width: 200px;
$topbar-unstack-breakpoint: medium;
// Internal variable that contains the flex justifying options
$-zf-flex-justify: -zf-flex-justify($global-text-direction);
$menu-items-padding: $space-one;
$xy-grid: false;
@@ -1,64 +1,22 @@
.bg-light {
@apply bg-slate-25 dark:bg-slate-800;
}
.flex-center {
@include flex-align(center, middle);
display: flex;
}
.bottom-space-fix {
margin-bottom: auto;
}
.full-height {
@include full-height();
}
// loader class
.spinner {
@include color-spinner();
display: inline-block;
height: $space-medium;
padding: $zero $space-medium;
position: relative;
vertical-align: middle;
width: $space-medium;
@apply inline-block h-6 py-0 px-6 relative align-middle w-6;
&.message {
@include normal-shadow;
background: $color-white;
border-radius: $space-large;
left: 0;
margin: $space-slab auto;
padding: $space-normal;
top: 0;
@apply bg-white dark:bg-slate-800 rounded-full left-0 my-3 mx-auto p-4 top-0;
&::before {
margin-left: -$space-slab;
margin-top: -$space-slab;
@apply -ml-3 -mt-3;
}
}
&.small {
height: $space-normal;
width: $space-normal;
@apply h-4 w-4;
&::before {
height: $space-normal;
margin-top: -$space-small;
width: $space-normal;
@apply h-4 -mt-2 w-4;
}
}
}
.justify-space-between {
justify-content: space-between;
}
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
@@ -1,5 +1,18 @@
// scss-lint:disable SpaceAfterPropertyColon
@import 'shared/assets/fonts/inter';
// Inter,
html,
body {
font-family:
'PlusJakarta',
-apple-system,
system-ui,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif !important;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
height: 100%;
@@ -131,37 +131,12 @@
}
}
.search-header--wrap {
.search--input {
text-align: right;
}
.layout-switch__container {
transform: rotate(180deg);
}
}
// Basic filter dropdown
.basic-filter {
left: 0;
right: unset;
}
// Card label
.label-container {
.label {
margin-left: var(--space-smaller);
margin-right: 0;
}
}
// Secondary sidebar toggle button
.toggle-sidebar {
margin-left: 0;
margin-right: var(--space-minus-small);
transform: rotate(180deg);
}
// Bulk actions
.bulk-action__container {
.triangle {
@@ -202,22 +177,6 @@
}
}
// Notification panel
.notification-wrap {
left: 0;
right: var(--space-jumbo);
.action-button {
margin-left: var(--space-small);
margin-right: 0;
}
.notification-content--wrap {
margin-left: 0;
margin-right: var(--space-small);
}
}
// Help center
.article-container .row--article-block {
td:last-child {
@@ -324,10 +283,6 @@
// Other changes
.account-selector--wrap {
direction: initial;
}
.colorpicker--chrome {
direction: initial;
}
@@ -347,9 +302,4 @@
.contact--form .input-group {
direction: initial;
}
// scss-lint:disable QualifyingElement
.dropdown-menu--header > span.title {
text-align: right;
}
}
@@ -1,33 +0,0 @@
.page-title {
font-size: $font-size-big;
}
.page-sub-title {
font-size: $font-size-large;
word-wrap: break-word;
}
.block-title {
font-size: $font-size-medium;
}
.sub-block-title {
font-size: $font-size-default;
}
.text-block-title {
font-size: $font-size-small;
}
.text-muted {
color: var(--s-300);
}
a {
font-size: $font-size-small;
}
p {
font-size: $font-size-small;
word-spacing: .12em;
}
@@ -1,73 +0,0 @@
.margin-bottom-small {
margin-bottom: var(--space-small);
}
.margin-right-smaller {
margin-right: var(--space-smaller);
}
.margin-left-minus-slab {
margin-left: var(--space-minus-slab);
}
.margin-right-minus-slab {
margin-right: var(--space-minus-slab);
}
.fs-small {
font-size: var(--font-size-small);
}
.fs-default {
font-size: var(--font-size-default);
}
.fw-medium {
font-weight: var(--font-weight-medium);
}
.p-normal {
padding: var(--space-normal);
}
.overflow-scroll {
overflow: scroll;
}
.overflow-auto {
overflow: auto;
}
.overflow-hidden {
overflow: hidden;
}
.border-right {
@apply border-r border-slate-50 dark:border-slate-700;
}
.border-left {
border-left: 1px solid var(--color-border);
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.flex-between {
align-items: center;
display: flex;
justify-content: space-between;
}
.flex-end {
display: flex;
justify-content: end;
}
.flex-align-center {
align-items: center;
display: flex;
}
@@ -15,63 +15,28 @@
@import 'variables';
@import 'mixins';
@import 'foundation-settings';
@import 'helper-classes';
@import 'formulate';
@import 'date-picker';
@import 'foundation-sites/scss/foundation';
@include foundation-everything($flex: true);
@include foundation-prototype-text-utilities;
@include foundation-prototype-text-transformation;
@include foundation-prototype-text-decoration;
@include foundation-prototype-font-styling;
@include foundation-prototype-list-style-type;
@include foundation-prototype-rounded;
@include foundation-prototype-bordered;
@include foundation-prototype-shadow;
@include foundation-prototype-separator;
@include foundation-prototype-overflow;
@include foundation-prototype-display;
@include foundation-prototype-position;
@include foundation-prototype-border-box;
@include foundation-prototype-border-none;
@include foundation-prototype-sizing;
@include foundation-prototype-spacing;
@import 'typography';
@import 'layout';
@import 'animations';
@import 'foundation-custom';
@import 'rtl';
@import 'widgets/base';
@import 'widgets/buttons';
@import 'widgets/conv-header';
@import 'widgets/conversation-card';
@import 'widgets/conversation-view';
@import 'widgets/forms';
@import 'widgets/login';
@import 'widgets/modal';
@import 'widgets/reply-box';
@import 'widgets/report';
@import 'widgets/search-box';
@import 'widgets/sidemenu';
@import 'widgets/snackbar';
@import 'widgets/states';
@import 'widgets/status-bar';
@import 'widgets/tabs';
@import 'widgets/woot-tables';
@import 'views/settings/inbox';
@import 'views/settings/integrations';
@import 'plugins/multiselect';
@import 'plugins/dropdown';
@import '~shared/assets/stylesheets/ionicons';
@import 'utility-helpers';
.tooltip {
@apply bg-slate-900 text-white py-1 px-2 z-40 text-xs rounded-md dark:bg-slate-200 dark:text-slate-900;
}
.hide {
@apply hidden;
}
@@ -1,46 +1,7 @@
.dropdown-pane {
@include elegant-card;
@include border-light;
box-sizing: content-box;
padding: var(--space-small);
width: fit-content;
z-index: var(--z-index-very-high);
@apply border rounded-lg hidden relative invisible shadow-lg border-slate-25 dark:border-slate-700 box-content p-2 w-fit z-[9999];
&.dropdown-pane--open {
@apply bg-white dark:bg-slate-800;
display: block;
visibility: visible;
}
&.dropdowm--bottom {
&::before {
@include arrow(top, var(--color-border-light), 14px);
position: absolute;
right: 6px;
top: -14px;
}
&::after {
@include arrow(top, $color-white, var(--space-slab));
position: absolute;
right: var(--space-small);
top: -12px;
}
}
&.dropdowm--top {
&::before {
@include arrow(bottom, var(--color-border-light), 14px);
bottom: -14px;
position: absolute;
right: 6px;
}
&::after {
@include arrow(bottom, $color-white, var(--space-slab));
bottom: -12px;
position: absolute;
right: var(--space-small);
}
@apply bg-white absolute dark:bg-slate-800 block visible;
}
}
@@ -97,6 +97,10 @@
.multiselect__tags {
@apply bg-white dark:bg-slate-900 border border-solid border-slate-200 dark:border-slate-600 m-0 min-h-[2.875rem] pt-0;
input {
@apply border-0 border-none;
}
}
.multiselect__tags-wrap {
@@ -149,7 +153,6 @@
}
.multiselect-wrap--small {
.multiselect__tags,
.multiselect__input,
.multiselect {
@@ -180,7 +183,6 @@
.multiselect--disabled .multiselect__select {
@apply bg-transparent;
}
}
.multiselect-wrap--medium {
@@ -13,20 +13,14 @@
@import '~shared/assets/stylesheets/ionicons';
@import 'mixins';
@import 'foundation-settings';
@import 'helper-classes';
@import 'foundation-sites/scss/foundation';
@include foundation-prototype-spacing;
@include foundation-everything($flex: true);
@import 'typography';
@import 'layout';
@import 'animations';
@import 'foundation-custom';
@import 'widgets/buttons';
@import 'widgets/forms';
@import 'widgets/base';
@import 'plugins/multiselect';
@@ -36,7 +30,6 @@
@import 'tailwindcss/utilities';
@import 'widget/assets/scss/utilities';
html,
body {
font-family: 'PlusJakarta', sans-serif;
@@ -1,112 +1 @@
.settings {
@apply overflow-auto;
}
.wizard-box {
.item {
@apply cursor-pointer py-4 pr-4 pl-6 relative;
&::before,
&::after {
@apply bg-slate-75 dark:bg-slate-600 content-[''] h-full absolute top-5 w-0.5;
}
&::before {
@apply h-4 top-0;
}
&:first-child {
&::before {
@apply h-0;
}
}
&:last-child {
&::after {
@apply h-0;
}
}
&.active {
h3 {
@apply text-woot-500 dark:text-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
}
&.over {
&::after {
@apply bg-woot-500 dark:bg-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
& + .item {
&::before {
@apply bg-woot-500 dark:bg-woot-500;
}
}
}
h3 {
@apply text-slate-800 dark:text-slate-100 text-base pl-6;
}
.completed {
@apply text-green-500 dark:text-green-500 ml-1;
}
p {
@apply text-slate-600 dark:text-slate-300 text-sm m-0 pl-6;
}
.step {
@apply bg-slate-75 dark:bg-slate-600 rounded-2xl font-medium w-4 left-4 leading-4 z-[999] absolute text-center text-white dark:text-white text-xxs top-5;
i {
@apply text-xxs;
}
}
}
}
.wizard-body {
@apply border border-slate-25 dark:border-slate-800/60 bg-white dark:bg-slate-900 h-full p-6;
&.height-auto {
@apply h-auto;
}
}
.settings--content {
@apply my-2 mx-8;
.title {
@apply font-medium;
}
.code {
@apply bg-slate-50 dark:bg-slate-800 overflow-auto p-2.5 whitespace-nowrap;
code {
@apply bg-transparent border-0;
}
}
}
.login-init {
@apply pt-[30%] text-center;
p {
@apply p-6;
}
> a > img {
@apply w-60;
}
}
// to be removed
@@ -0,0 +1,146 @@
// scss-lint:disable QualifyingElement
// Base typography
h1,
h2,
h3,
h4,
h5,
h6 {
@apply font-medium text-slate-800 dark:text-slate-50;
}
p {
text-rendering: optimizeLegibility;
word-spacing: 0.12em;
@apply mb-2 leading-[1.65] text-sm;
a {
@apply text-woot-500 dark:text-woot-500 cursor-pointer;
}
}
a {
@apply text-sm;
}
hr {
@apply clear-both max-w-full h-0 my-5 mx-0 border-slate-300 dark:border-slate-600;
}
ul,
ol,
dl {
@apply mb-2 list-disc list-outside leading-[1.65];
}
// Form elements
label {
@apply text-slate-800 dark:text-slate-200 block m-0 leading-7 text-sm font-medium;
&.error {
input {
@apply mb-1;
}
}
}
.input-wrap,
.help-text {
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
.help-text {
@apply font-normal text-slate-600 dark:text-slate-400;
}
}
// Focus outline removal
.button,
textarea,
input:focus {
outline: none;
}
// Inputs
input[type='text'],
input[type='number'],
input[type='password'],
input[type='date'],
input[type='email'],
input[type='url'] {
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-10 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
}
}
input[type='file'] {
@apply bg-white dark:bg-slate-800 leading-[1.15] mb-4;
}
// Select
select {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='32' height='24' viewBox='0 0 32 24'><polygon points='0,0 32,0 16,24' style='fill: rgb%28110, 111, 115%29'></polygon></svg>");
background-position: right -1rem center;
background-size: 9px 6px;
@apply h-10 mx-0 mt-0 mb-4 bg-origin-content focus-visible:outline-none bg-no-repeat py-2 pr-6 pl-2 rounded-md w-full text-base font-normal appearance-none transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
}
// Textarea
textarea {
@apply block box-border w-full transition-colors focus:border-woot-500 dark:focus:border-woot-600 duration-[0.25s] ease-[ease-in-out] h-16 appearance-none mx-0 mt-0 mb-4 p-2 rounded-md text-base font-normal bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border border-solid border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600 cursor-not-allowed;
}
}
// Error handling
.has-multi-select-error {
div.multiselect {
@apply mb-1;
}
}
.error {
input,
input:not([type]),
textarea,
select,
.multiselect > .multiselect__tags,
.multiselect:not(.no-margin) {
@apply border border-solid border-red-400 dark:border-red-400 mb-1;
}
.message {
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
}
}
.input-group.small {
input {
@apply text-sm h-8;
}
.error {
@apply border-red-400 dark:border-red-400;
}
}
// Code styling
code {
font-family: 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas',
'"Liberation Mono"', '"Courier New"', 'monospace';
@apply text-xs border-0;
&.hljs {
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-50 rounded-lg p-5;
.hljs-number,
.hljs-string {
@apply text-red-800 dark:text-red-400;
}
}
}
@@ -1,8 +1,40 @@
// 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 inline-flex h-10 mb-0 gap-2;
@apply items-center bg-woot-500 dark:bg-woot-500 px-2.5 text-white dark:text-white inline-flex h-10 mb-0 gap-2 font-medium;
.button__content {
@apply w-full;
@apply w-full whitespace-nowrap overflow-hidden text-ellipsis;
img,
svg {
@@ -10,12 +42,61 @@
}
}
&:hover {
@apply bg-woot-600 dark:bg-woot-600;
}
&:disabled,
&.disabled {
@apply opacity-40 cursor-not-allowed;
}
&.success {
@apply bg-[#44ce4b] dark:bg-[#44ce4b] text-white dark:text-white;
}
&.secondary {
@apply bg-slate-700 dark:bg-slate-600 text-white dark:text-white;
}
&.primary {
@apply bg-woot-500 dark:bg-woot-500 text-white dark:text-white;
}
&.clear {
@apply text-woot-500 dark:text-woot-500 bg-transparent dark:bg-transparent;
}
&.alert {
@apply bg-red-500 dark:bg-red-500 text-white dark:text-white;
&.clear {
@apply bg-transparent dark:bg-transparent;
}
}
&.warning {
@apply bg-[#ffc532] dark:bg-[#ffc532] 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 {
.icon+.button__content {
@apply w-auto;
}
@@ -34,7 +115,7 @@
}
&.hollow {
@apply border border-woot-500 dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
@apply border border-woot-500 bg-transparent dark:bg-transparent dark:border-woot-500 text-woot-500 dark:text-woot-500 hover:bg-woot-50 dark:hover:bg-woot-900;
&.secondary {
@apply text-slate-700 border-slate-200 dark:border-slate-600 dark:text-slate-100 hover:bg-slate-50 dark:hover:bg-slate-700;
@@ -1 +0,0 @@
// File to be removed
@@ -1,16 +0,0 @@
@keyframes left-shift-animation {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(1px);
}
}
.conversation {
&.active {
animation: left-shift-animation 0.25s $swift-ease-out-function;
}
}
@@ -79,7 +79,7 @@
@apply rounded-r-lg rounded-l mr-auto break-words;
&:not(.is-unsupported) {
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50
@apply border border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-700 text-black-900 dark:text-slate-50;
}
&.is-image {
@@ -91,7 +91,7 @@
}
.file {
.text-block-title {
.attachment-name {
@apply text-slate-700 dark:text-woot-300;
}
@@ -222,20 +222,6 @@
@apply flex relative flex-col;
}
.typing-indicator-wrap {
@apply items-center flex h-0 absolute w-full -top-8;
.typing-indicator {
@include elegant-card;
@include round-corner;
@apply py-2 pr-4 pl-5 bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-100 text-xs font-semibold my-2.5 mx-auto;
.gif {
@apply ml-2 w-6;
}
}
}
.left .bubble .text-content {
h1,
h2,
@@ -1,78 +0,0 @@
// scss-lint:disable QualifyingElement
label {
@apply text-slate-800 dark:text-slate-200;
}
textarea {
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
}
input {
@apply bg-white dark:bg-slate-900 focus:bg-white focus:dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
&[disabled] {
@apply bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-400 border-slate-200 dark:border-slate-600;
}
}
input[type='file'] {
@apply bg-white dark:bg-slate-800;
}
select {
@apply bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600;
}
.error {
input[type='color'],
input[type='date'],
input[type='datetime'],
input[type='datetime-local'],
input[type='email'],
input[type='month'],
input[type='number'],
input[type='password'],
input[type='search'],
input[type='tel'],
input[type='text'],
input[type='time'],
input[type='url'],
input[type='week'],
input:not([type]),
textarea,
select,
.multiselect > .multiselect__tags {
@apply border border-solid border-red-400 dark:border-red-400;
}
.message {
@apply text-red-400 dark:text-red-400 block text-sm mb-2.5 w-full;
}
}
.button,
textarea,
input {
&:focus {
outline: none;
}
}
.input-wrap {
@apply text-slate-800 dark:text-slate-100 text-sm font-medium;
}
.help-text {
@apply font-normal text-slate-600 dark:text-slate-400;
}
.input-group.small {
input {
@apply text-sm h-8;
}
.error {
@apply border-red-400 dark:border-red-400;
}
}
@@ -1,66 +0,0 @@
.auth-wrap {
width: 100%;
}
// Outside login wrapper
.login {
@include full-height;
overflow-y: auto;
padding-top: $space-larger * 1.2;
.login__hero {
margin-bottom: $space-larger;
.hero__logo {
width: 180px;
}
.hero__title {
font-weight: $font-weight-light;
margin-top: $space-larger;
}
.hero__sub {
color: $medium-gray;
font-size: $font-size-medium;
}
}
// Login box
.login-box {
@include background-white;
@include border-normal;
@include elegant-card;
border-radius: $space-smaller;
padding: $space-large;
label {
color: $color-gray;
font-size: $font-size-default;
input {
font-size: $font-size-default;
height: $space-larger;
padding: $space-slab;
}
.error {
font-size: $font-size-small;
}
}
.button {
height: $space-larger;
}
}
.sigin__footer {
font-size: $font-size-default;
padding: $space-medium;
> a {
font-weight: $font-weight-bold;
}
}
}
@@ -1,75 +0,0 @@
.modal-mask {
// @include flex;
// @include flex-align(center, middle);
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
}
.page-top-bar {
@apply px-8 pt-9 pb-0;
img {
@apply max-h-[3.75rem];
}
}
.modal-container {
@apply shadow-md rounded-sm max-h-full overflow-auto relative w-[37.5rem];
&.medium {
@apply max-w-[80%] w-[56.25rem];
}
.content-box {
@apply h-auto p-0;
}
h2 {
@apply text-slate-800 dark:text-slate-100 text-lg font-semibold;
}
p {
@apply text-sm m-0 p-0 text-slate-600 mt-2 text-sm dark:text-slate-300;
}
.content {
@apply p-8;
}
form,
.modal-content {
@apply pt-4 pb-8 px-8 self-center;
a {
@apply p-4;
}
}
.modal-footer {
// @include flex;
// @include flex-align($x: flex-end, $y: middle);
@apply flex justify-end items-center py-2 px-0 gap-2;
&.justify-content-end {
@apply justify-end;
}
}
.delete-item {
@apply p-8;
button {
@apply m-0;
}
}
}
.modal-enter,
.modal-leave {
@apply opacity-0;
}
.modal-enter .modal-container,
.modal-leave .modal-container {
transform: scale(1.1);
// @apply transform scale-110;
}
@@ -1,60 +0,0 @@
.reply-box {
transition: box-shadow 0.35s $swift-ease-out-function,
height 2s $swift-ease-out-function;
&.is-focused {
box-shadow: var(--shadow);
}
.reply-box__top {
.icon {
color: $medium-gray;
cursor: pointer;
font-size: $font-size-medium;
margin-right: $space-small;
&.active {
color: $color-woot;
}
}
.attachment {
cursor: pointer;
margin-right: $space-one;
padding: 0 $space-small;
}
.video-js {
background: transparent;
// Override min-height : 50px in foundation
//
max-height: $space-mega * 2.4;
min-height: 3rem;
padding: var(--space-normal) 0 0;
resize: none;
}
> textarea {
@include ghost-input();
background: transparent;
margin: 0;
max-height: $space-mega * 2.4;
// Override min-height : 50px in foundation
min-height: 3rem;
padding: var(--space-normal) 0 0;
resize: none;
}
}
&.is-private {
@apply bg-yellow-100 dark:bg-yellow-800;
.reply-box__top {
@apply bg-yellow-100 dark:bg-yellow-800;
> input {
@apply bg-yellow-100 dark:bg-yellow-800;
}
}
}
}
@@ -1,60 +0,0 @@
.report-card {
@include custom-border-top(3px, transparent);
cursor: pointer;
margin: 0;
padding: var(--space-normal);
&.active {
@include custom-border-top(3px, var(--color-woot));
@include background-white;
.heading,
.metric {
color: var(--color-woot);
}
}
.heading {
align-items: center;
color: var(--color-heading);
display: flex;
font-size: var(--font-size-small);
font-weight: var(--font-weight-bold);
margin: 0;
}
.info-icon {
color: var(--b-400);
margin-left: var(--space-micro);
}
.metric-wrap {
align-items: center;
display: flex;
}
.metric {
font-size: var(--font-size-big);
font-weight: var(--font-weight-feather);
margin-top: var(--space-smaller);
}
.metric-trend {
font-size: var(--font-size-small);
margin: 0 var(--space-small);
}
.metric-up {
color: $success-color;
}
.metric-down {
color: $alert-color;
}
.desc {
font-size: var(--font-size-small);
margin: 0;
text-transform: capitalize;
}
}
@@ -1,29 +0,0 @@
.reports-option__rounded--item {
border-radius: 100%;
height: var(--space-two);
width: var(--space-two);
}
.reports-option__item {
flex-shrink: 0;
margin-right: var(--space-small);
}
.reports-option__label--swatch {
border: 1px solid var(--color-border);
}
.reports-option__wrap {
align-items: center;
display: flex;
}
.reports-option__title {
margin: 0 var(--space-small);
}
.switch {
margin-bottom: var(--space-zero);
margin-left: var(--space-small);
}
@@ -1,18 +0,0 @@
.search {
@include flex;
@include flex-align($x: left, $y: middle);
@include flex-shrink;
padding: $space-one $space-normal;
transition: all 0.3s var(--ease-in-out-quad);
> .icon {
color: $medium-gray;
font-size: $font-size-medium;
}
> input {
@include ghost-input();
margin: 0;
}
}
@@ -1,78 +0,0 @@
.side-menu {
i {
margin-right: var(--space-smaller);
min-width: var(--space-two);
}
}
.sidebar {
z-index: 1024 - 1;
//logo
.logo {
img {
max-height: 108px;
padding: $woot-logo-padding;
}
}
.nested {
a {
font-size: var(--font-size-small);
margin-bottom: var(--space-micro);
margin-top: var(--space-micro);
.inbox-icon {
display: inline-block;
margin-right: var(--space-micro);
min-width: var(--space-normal);
text-align: center;
}
}
}
}
// bottom-nav
.bottom-nav {
@include flex;
@include space-between-column;
@include border-normal-top;
flex-direction: column;
padding: var(--space-one) var(--space-normal) var(--space-one)
var(--space-one);
position: relative;
&:hover {
background: var(--color-background-light);
}
.dropdown-pane {
bottom: 3.75rem;
display: block;
visibility: visible;
width: fit-content;
}
.active {
border-bottom: 2px solid $medium-gray;
}
}
.hamburger--menu {
cursor: pointer;
display: block;
margin-right: var(--space-normal);
}
.header--icon {
display: block;
margin: 0 var(--space-small) 0 var(--space-smaller);
@media screen and (max-width: 1200px) {
display: none;
}
}
.header-title {
margin: 0 var(--space-small);
}
@@ -1,45 +0,0 @@
.ui-snackbar-container {
left: 0;
margin: 0 auto;
max-width: 25rem;
overflow: hidden;
position: absolute;
right: 0;
text-align: center;
top: $space-normal;
z-index: 9999;
}
.ui-snackbar {
@include shadow;
background-color: $woot-snackbar-bg;
border-radius: $space-smaller;
display: inline-flex;
margin-bottom: $space-small;
max-width: 25rem;
min-height: 1.875rem;
min-width: 15rem;
padding: $space-slab $space-medium;
text-align: left;
}
.ui-snackbar-text {
color: $color-white;
font-size: $font-size-small;
font-weight: $font-weight-medium;
}
.ui-snackbar-action {
margin-left: auto;
padding-left: 1.875rem;
button {
background: none;
border: 0;
color: $woot-snackbar-button;
font-size: $font-size-small;
margin: 0;
padding: 0;
text-transform: uppercase;
}
}
@@ -1 +0,0 @@
// To be removed
@@ -1,46 +0,0 @@
.status-bar {
@include flex;
@include flex-align($x: center, $y: middle);
background: lighten($warning-color, 36%);
flex-direction: column;
margin: 0;
padding: $space-normal $space-smaller;
.message {
font-weight: $font-weight-medium;
margin-bottom: $zero;
}
.button {
margin: $space-smaller $zero $zero;
padding: $space-small $space-normal;
}
&.danger {
background: lighten($alert-color, 30%);
.button {
// Default and disabled states
&,
&.disabled,
&[disabled],
&.disabled:hover,
&[disabled]:hover,
&.disabled:focus,
&[disabled]:focus {
background-color: $alert-color;
color: $color-white;
}
&:hover,
&:focus {
background-color: darken($alert-color, 7%);
color: $color-white;
}
}
}
&.warning {
background: lighten($warning-color, 36%);
}
}
@@ -7,7 +7,7 @@
}
.tabs {
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4;
@apply border-r-0 border-l-0 border-t-0 flex min-w-[6.25rem] py-0 px-4 list-none mb-0;
}
.tabs--with-scroll {
@@ -31,7 +31,7 @@
}
.tabs-title {
@apply flex-shrink-0 my-0 mx-2 ;
@apply flex-shrink-0 my-0 mx-2;
.badge {
@apply bg-slate-50 dark:bg-slate-800 rounded-md text-slate-600 dark:text-slate-100 h-5 flex items-center justify-center text-xxs font-semibold my-0 mx-1 px-1 py-0;
@@ -53,7 +53,7 @@
}
a {
@apply flex items-center flex-row border-b border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
@apply flex items-center flex-row border-b py-2.5 select-none cursor-pointer border-transparent text-slate-500 dark:text-slate-200 text-sm top-[1px] relative;
transition: border-color 0.15s $swift-ease-out-function;
}
@@ -1,9 +1,9 @@
table {
@apply border-spacing-0 text-sm;
@apply border-spacing-0 text-sm w-full;
thead {
th {
@apply font-semibold tracking-[1px] text-left uppercase text-slate-900 dark:text-slate-200;
@apply font-semibold tracking-[1px] text-left px-2.5 uppercase text-slate-900 dark:text-slate-200;
}
}
@@ -1,7 +1,7 @@
<template>
<div class="-mt-px text-sm">
<button
class="flex items-center select-none w-full bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
class="flex items-center select-none w-full rounded-none bg-slate-50 dark:bg-slate-800 border border-l-0 border-r-0 border-solid m-0 border-slate-100 dark:border-slate-700/50 cursor-grab justify-between py-2 px-4 drag-handle"
@click="$emit('click')"
>
<div class="flex justify-between mb-0.5">
@@ -1,10 +1,12 @@
<template>
<button
class="sm:w-[50%] md:w-[34%] lg:w-[25%] channel"
class="sm:w-[50%] md:w-1/3 lg:w-1/4 bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800 hover:border-woot-500 dark:hover:border-woot-500 hover:shadow-md hover:z-50 disabled:opacity-60"
@click="$emit('click')"
>
<img :src="src" :alt="title" />
<h3 class="channel__title">
<img :src="src" :alt="title" class="w-[50%] my-4 mx-auto" />
<h3
class="text-slate-800 dark:text-slate-100 text-base text-center capitalize"
>
{{ title }}
</h3>
</button>
@@ -31,32 +33,8 @@ export default {
filter: grayscale(100%);
}
&.channel:hover {
&:hover {
@apply border-transparent shadow-none cursor-not-allowed;
}
}
.channel {
@apply bg-white dark:bg-slate-900 cursor-pointer flex flex-col transition-all duration-200 ease-in -m-px py-4 px-0 items-center border border-solid border-slate-25 dark:border-slate-800;
&:hover {
@apply border-woot-500 dark:border-woot-500 shadow-md z-50;
}
&.disabled {
@apply opacity-60;
}
img {
@apply w-[50%] my-4 mx-auto;
}
.channel__title {
@apply text-slate-800 dark:text-slate-100 text-base text-center capitalize;
}
p {
@apply text-slate-600 dark:text-slate-300 w-full text-sm;
}
}
</style>
@@ -16,7 +16,7 @@
>
<div class="flex max-w-[85%] justify-center items-center">
<h1
class="text-xl break-words overflow-hidden whitespace-nowrap text-ellipsis text-black-900 dark:text-slate-100 mb-0"
class="text-xl break-words overflow-hidden whitespace-nowrap font-medium text-ellipsis text-black-900 dark:text-slate-100 mb-0"
:title="pageTitle"
>
{{ pageTitle }}
@@ -143,7 +143,10 @@
<div v-if="chatListLoading" class="text-center">
<span class="spinner mt-4 mb-4" />
</div>
<p v-if="showEndOfListMessage" class="text-center text-muted p-4">
<p
v-if="showEndOfListMessage"
class="text-center text-slate-400 dark:text-slate-300 p-4"
>
{{ $t('CHAT_LIST.EOF') }}
</p>
<intersection-observer
@@ -1,7 +1,7 @@
<template>
<div class="py-3 px-4">
<div class="flex items-center mb-1">
<h4 class="text-block-title flex items-center m-0 w-full error">
<h4 class="text-sm flex items-center m-0 w-full error">
<div v-if="isAttributeTypeCheckbox" class="checkbox-wrap">
<input
v-model="editedValue"
@@ -12,7 +12,7 @@
</div>
<div class="flex items-center justify-between w-full">
<span
class="attribute-name"
class="attribute-name w-full text-slate-800 dark:text-slate-100 font-medium text-sm mb-0"
:class="{ error: $v.editedValue.$error }"
>
{{ label }}
@@ -32,19 +32,24 @@
</div>
<div v-if="notAttributeTypeCheckboxAndList">
<div v-show="isEditing">
<div class="input-group small">
<div class="mb-2 w-full flex items-center">
<input
ref="inputfield"
v-model="editedValue"
:type="inputType"
class="input-group-field"
class="!h-8 ltr:rounded-r-none rtl:rounded-l-none !mb-0 !text-sm"
autofocus="true"
:class="{ error: $v.editedValue.$error }"
@blur="$v.editedValue.$touch"
@keyup.enter="onUpdate"
/>
<div class="input-group-button">
<woot-button size="small" icon="checkmark" @click="onUpdate" />
<div>
<woot-button
size="small"
icon="checkmark"
class="rounded-l-none rtl:rounded-r-none"
@click="onUpdate"
/>
</div>
</div>
<span
@@ -306,7 +311,6 @@ export default {
@apply my-0 mr-2 ml-0;
}
.attribute-name {
@apply w-full text-slate-800 dark:text-slate-100;
&.error {
@apply text-red-400 dark:text-red-500;
}
+36 -19
View File
@@ -62,9 +62,9 @@ export default {
computed: {
modalContainerClassName() {
let className =
'modal-container bg-white dark:bg-slate-800 skip-context-menu';
'modal-container rtl:text-right shadow-md rounded-sm max-h-full overflow-auto relative w-[37.5rem] bg-white dark:bg-slate-800 skip-context-menu';
if (this.fullWidth) {
return `${className} modal-container--full-width`;
return `${className} items-center rounded-none flex h-full justify-center w-full`;
}
return `${className} ${this.size}`;
@@ -109,26 +109,43 @@ export default {
};
</script>
<style scoped lang="scss">
.modal-container--full-width {
align-items: center;
border-radius: 0;
display: flex;
height: 100%;
justify-content: center;
width: 100%;
}
.modal-mask.right-aligned {
justify-content: flex-end;
<style lang="scss">
.modal-mask {
@apply flex items-center justify-center bg-modal-backdrop-light dark:bg-modal-backdrop-dark z-[9990] h-full left-0 fixed top-0 w-full;
.modal-container {
border-radius: 0;
height: 100%;
width: 30rem;
&.medium {
@apply max-w-[80%] w-[56.25rem];
}
// .content-box {
// @apply h-auto p-0;
// }
.content {
@apply p-8;
}
form,
.modal-content {
@apply pt-4 pb-8 px-8 self-center;
a {
@apply p-4;
}
}
}
}
.modal-big {
width: 60%;
@apply w-full;
}
.modal-mask.right-aligned {
@apply justify-end;
.modal-container {
@apply rounded-none h-full w-[30rem];
}
}
.modal-enter,
.modal-leave {
@apply opacity-0;
}
.modal-enter .modal-container,
.modal-leave .modal-container {
transform: scale(1.1);
}
</style>
@@ -3,7 +3,7 @@
<img v-if="headerImage" :src="headerImage" alt="No image" />
<h2
ref="modalHeaderTitle"
class="text-slate-800 text-lg dark:text-slate-100"
class="text-slate-800 text-lg font-semibold dark:text-slate-50"
>
{{ headerTitle }}
</h2>
@@ -1,19 +1,21 @@
<template>
<div
class="ml-0 mr-0 flex pt-0 pr-4 pb-4 pl-0"
class="ml-0 mr-0 flex py-8 w-full xl:w-3/4 flex-col xl:flex-row"
:class="{
'pt-4 border-b border-solid border-slate-50 dark:border-slate-700/30':
'border-b border-solid border-slate-50 dark:border-slate-700/30':
showBorder,
}"
>
<div class="w-[30%] min-w-0 max-w-[30%] pr-12">
<div class="w-full xl:w-1/4 min-w-0 xl:max-w-[30%] pr-12">
<p
v-if="title"
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
>
{{ title }}
</p>
<p class="text-sm mb-2">
<p
class="text-sm mb-2 text-slate-700 dark:text-slate-300 leading-5 tracking-normal mt-2"
>
<slot v-if="subTitle" name="subTitle">
{{ subTitle }}
</slot>
@@ -23,7 +25,7 @@
{{ note }}
</p>
</div>
<div class="w-[50%] min-w-0 max-w-[50%]">
<div class="w-full xl:w-1/2 min-w-0 xl:max-w-[50%]">
<slot />
</div>
</div>
@@ -1,11 +1,17 @@
<template>
<div>
<div class="ui-snackbar">
<div class="ui-snackbar-text">
<div
class="shadow-sm bg-slate-800 dark:bg-slate-700 rounded-[4px] items-center gap-3 inline-flex mb-2 max-w-[25rem] min-h-[1.875rem] min-w-[15rem] px-6 py-3 text-left"
>
<div class="text-white dark:text-white text-sm font-medium">
{{ message }}
</div>
<div v-if="action" class="ui-snackbar-action">
<router-link v-if="action.type == 'link'" :to="action.to">
<div v-if="action">
<router-link
v-if="action.type == 'link'"
:to="action.to"
class="text-woot-500 dark:text-woot-500 cursor-pointer font-medium hover:text-woot-600 dark:hover:text-woot-600 select-none"
>
{{ action.message }}
</router-link>
</div>
@@ -1,5 +1,9 @@
<template>
<transition-group name="toast-fade" tag="div" class="ui-snackbar-container">
<transition-group
name="toast-fade"
tag="div"
class="left-0 my-0 mx-auto max-w-[25rem] overflow-hidden absolute right-0 text-center top-4 z-[9999]"
>
<woot-snackbar
v-for="snackMessage in snackMessages"
:key="snackMessage.key"
@@ -48,7 +48,7 @@
v-on-clickaway="closeDropdown"
class="dropdown-pane dropdown-pane--open"
>
<woot-dropdown-menu>
<woot-dropdown-menu class="mb-0">
<woot-dropdown-item v-if="!isPending">
<woot-button
variant="clear"
@@ -18,7 +18,6 @@ import Label from './ui/Label';
import LoadingState from './widgets/LoadingState';
import Modal from './Modal';
import ModalHeader from './ModalHeader';
import ReportStatsCard from './widgets/ReportStatsCard';
import SidemenuIcon from './SidemenuIcon';
import Spinner from 'shared/components/Spinner';
import SubmitButton from './buttons/FormSubmitButton';
@@ -46,7 +45,6 @@ const WootUIKit = {
LoadingState,
Modal,
ModalHeader,
ReportStatsCard,
SidemenuIcon,
Spinner,
SubmitButton,
@@ -2,7 +2,7 @@ import { frontendURL } from '../../../../helper/URLHelper';
const campaigns = accountId => ({
parentNav: 'campaigns',
routes: ['settings_account_campaigns', 'one_off'],
routes: ['ongoing_campaigns', 'one_off'],
menuItems: [
{
icon: 'arrow-swap',
@@ -10,7 +10,7 @@ const campaigns = accountId => ({
key: 'ongoingCampaigns',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/campaigns/ongoing`),
toStateName: 'settings_account_campaigns',
toStateName: 'ongoing_campaigns',
},
{
key: 'oneOffCampaigns',
@@ -2,6 +2,15 @@ import { FEATURE_FLAGS } from '../../../../featureFlags';
import { frontendURL } from '../../../../helper/URLHelper';
const primaryMenuItems = accountId => [
{
icon: 'mail-inbox',
key: 'inboxView',
label: 'INBOX_VIEW',
featureFlag: FEATURE_FLAGS.INBOX_VIEW,
toState: frontendURL(`accounts/${accountId}/inbox-view`),
toStateName: 'inbox_view',
roles: ['administrator', 'agent'],
},
{
icon: 'chat',
key: 'conversations',
@@ -34,7 +43,7 @@ const primaryMenuItems = accountId => [
label: 'CAMPAIGNS',
featureFlag: FEATURE_FLAGS.CAMPAIGNS,
toState: frontendURL(`accounts/${accountId}/campaigns`),
toStateName: 'settings_account_campaigns',
toStateName: 'ongoing_campaigns',
roles: ['administrator'],
},
{
@@ -1,3 +1,4 @@
import { FEATURE_FLAGS } from '../../../../featureFlags';
import { frontendURL } from '../../../../helper/URLHelper';
const reports = accountId => ({
@@ -6,6 +7,7 @@ const reports = accountId => ({
'account_overview_reports',
'conversation_reports',
'csat_reports',
'bot_reports',
'agent_reports',
'label_reports',
'inbox_reports',
@@ -33,6 +35,14 @@ const reports = accountId => ({
toState: frontendURL(`accounts/${accountId}/reports/csat`),
toStateName: 'csat_reports',
},
{
icon: 'bot',
label: 'REPORTS_BOT',
hasSubMenu: false,
featureFlag: FEATURE_FLAGS.RESPONSE_BOT,
toState: frontendURL(`accounts/${accountId}/reports/bot`),
toStateName: 'bot_reports',
},
{
icon: 'people',
label: 'REPORTS_AGENT',
@@ -39,6 +39,7 @@ const settings = accountId => ({
'settings_teams_finish',
'settings_teams_list',
'settings_teams_new',
'sla_list',
],
menuItems: [
{
@@ -158,6 +159,15 @@ const settings = accountId => ({
featureFlag: FEATURE_FLAGS.AUDIT_LOGS,
beta: true,
},
{
icon: 'document-list-clock',
label: 'SLA',
hasSubMenu: false,
toState: frontendURL(`accounts/${accountId}/settings/sla/list`),
toStateName: 'sla_list',
featureFlag: FEATURE_FLAGS.SLA,
beta: true,
},
],
});
@@ -45,7 +45,7 @@
<div
v-if="globalConfig.createNewAccountFromDashboard"
class="modal-footer delete-item"
class="flex justify-end items-center p-8 gap-2"
>
<button
class="button success large expanded nice"
@@ -2,7 +2,7 @@
<woot-button
v-tooltip.right="$t(`SIDEBAR.PROFILE_SETTINGS`)"
variant="link"
class="current-user"
class="items-center flex rounded-full"
@click="handleClick"
>
<thumbnail
@@ -38,12 +38,3 @@ export default {
},
};
</script>
<style scoped lang="scss">
.current-user {
align-items: center;
display: flex;
border-radius: 50%;
border: 2px solid var(--white);
}
</style>
@@ -1,7 +1,7 @@
<template>
<div class="mb-4">
<button
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
class="text-slate-600 dark:text-slate-100 w-10 h-10 my-2 p-0 flex items-center justify-center rounded-lg hover:bg-slate-25 dark:hover:bg-slate-700 dark:hover:text-slate-100 hover:text-slate-600 relative"
:class="{
'bg-woot-50 dark:bg-slate-800 text-woot-500 hover:bg-woot-50':
isNotificationPanelActive,
@@ -16,7 +16,7 @@
/>
<span
v-if="unreadCount"
class="text-black-900 bg-yellow-300 absolute -top-0.5 -right-1 p-1 text-xxs min-w-[1rem] rounded-full"
class="text-black-900 bg-yellow-300 absolute -top-0.5 -right-1 text-xxs min-w-[1rem] rounded-full"
>
{{ unreadCount }}
</span>
@@ -53,7 +53,7 @@
</span>
<span
v-if="showChildCount"
class="bg-slate-50 dark:bg-slate-700 rounded text-xxs font-medium mx-1 py-0 px-1"
class="bg-slate-50 dark:bg-slate-700 rounded-full min-w-[18px] justify-center items-center flex text-xxs font-medium mx-1 py-0 px-1"
:class="
isCountZero
? `text-slate-300 dark:text-slate-500`
@@ -1,18 +1,25 @@
<template>
<div :class="labelClass" :style="labelStyle" :title="description">
<div
class="ltr:mr-1 rtl:ml-1 mb-1"
:class="labelClass"
:style="labelStyle"
:title="description"
>
<span v-if="icon" class="label-action--button">
<fluent-icon :icon="icon" size="12" class="label--icon cursor-pointer" />
</span>
<span
v-if="['smooth', 'dashed'].includes(variant) && title && !icon"
:style="{ background: color }"
class="label-color-dot"
class="label-color-dot flex-shrink-0"
/>
<span v-if="!href">{{ title }}</span>
<span v-if="!href" class="whitespace-nowrap text-ellipsis overflow-hidden">
{{ title }}
</span>
<a v-else :href="href" :style="anchorStyle">{{ title }}</a>
<button
v-if="showClose"
class="label-close--button"
class="label-close--button p-0"
:style="{ color: textColor }"
@click="onClick"
>
@@ -104,7 +111,7 @@ export default {
<style scoped lang="scss">
.label {
@apply inline-flex items-center font-medium gap-1 mr-1 rtl:ml-1 rtl:mr-0 mb-1 p-1 bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100 border border-solid border-slate-75 dark:border-slate-600 h-6;
@apply inline-flex items-center font-medium text-xs rounded-[4px] gap-1 p-1 bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-100 border border-solid border-slate-75 dark:border-slate-600 h-6;
&.small {
@apply text-xs py-0.5 px-1 leading-tight h-5;
@@ -1,6 +1,18 @@
<template>
<div class="preview-card--wrap" :class="{ 'active-card': active }">
<div class="header--wrap" :class="{ active: active }">
<div
class="flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-slate-75 dark:border-slate-600"
:class="{
'bg-woot-25 dark:bg-slate-700 border border-solid border-woot-300 dark:border-woot-400':
active,
}"
>
<div
class="flex justify-between items-center px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 rounded-t-[5px] border-b border-solid border-slate-50 dark:border-slate-600"
:class="{
'bg-woot-50 border-b border-solid border-woot-75 dark:border-woot-700':
active,
}"
>
<div class="items-center flex font-medium p-1 text-sm">{{ heading }}</div>
<fluent-icon
v-if="active"
@@ -56,21 +68,3 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.preview-card--wrap {
@apply flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-slate-75 dark:border-slate-600;
.header--wrap {
@apply flex justify-between items-center px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 rounded-t-[5px] border-b border-solid border-slate-50 dark:border-slate-600;
}
.active {
@apply bg-woot-50 border-b border-solid border-woot-75 dark:border-woot-700;
}
}
.active-card {
@apply bg-woot-25 dark:bg-slate-700 border border-solid border-woot-300 dark:border-woot-400;
}
</style>
@@ -1,7 +1,7 @@
<template>
<button
type="button"
class="toggle-button"
class="toggle-button p-0"
:class="{ active: value, small: size === 'small' }"
role="switch"
:aria-checked="value.toString()"
@@ -7,7 +7,7 @@
>
<a @click="onTabClick">
{{ name }}
<div v-if="showBadge" class="badge">
<div v-if="showBadge" class="badge min-w-[20px]">
<span>
{{ getItemCount }}
</span>
@@ -13,18 +13,23 @@
>
<div class="flex items-center">
<h3
class="overflow-hidden whitespace-nowrap text-ellipsis leading-tight"
class="text-slate-800 dark:text-slate-100 text-base font-medium pl-6 overflow-hidden whitespace-nowrap mb-1.5 text-ellipsis leading-tight"
>
{{ item.title }}
</h3>
<span v-if="isOver(item)" class="completed">
<span
v-if="isOver(item)"
class="text-green-500 dark:text-green-500 ml-1"
>
<fluent-icon icon="checkmark" />
</span>
</div>
<span class="step">
{{ items.indexOf(item) + 1 }}
</span>
<p>{{ item.body }}</p>
<p class="text-slate-600 dark:text-slate-300 text-sm m-0 pl-6">
{{ item.body }}
</p>
</div>
</transition-group>
</template>
@@ -60,3 +65,40 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.wizard-box {
.item {
@apply cursor-pointer after:bg-slate-75 before:bg-slate-75 dark:after:bg-slate-600 dark:before:bg-slate-600 py-4 pr-4 pl-6 relative before:h-4 before:top-0 last:before:h-0 first:before:h-0 last:after:h-0 before:content-[''] before:absolute before:w-0.5 after:content-[''] after:h-full after:absolute after:top-5 after:w-0.5;
&.active {
h3 {
@apply text-woot-500 dark:text-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
}
&.over {
&::after {
@apply bg-woot-500 dark:bg-woot-500;
}
.step {
@apply bg-woot-500 dark:bg-woot-500;
}
& + .item {
&::before {
@apply bg-woot-500 dark:bg-woot-500;
}
}
}
.step {
@apply bg-slate-75 dark:bg-slate-600 rounded-2xl font-medium w-4 left-4 leading-4 z-[999] absolute text-center text-white dark:text-white text-xxs top-5;
}
}
}
</style>
@@ -15,7 +15,7 @@
📄
</span>
</div>
<div class="max-w-[60%] min-w-[50%] overflow-hidden text-ellipsis">
<div class="max-w-3/5 min-w-[50%] overflow-hidden text-ellipsis">
<span
class="h-4 overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap"
>
@@ -1,6 +1,6 @@
<template>
<button
class="header-section flex items-center text-base font-normal mr-4 ml-2 cursor-pointer text-woot-500 dark:text-woot-500"
class="header-section flex items-center text-base font-normal mr-4 ml-2 p-0 cursor-pointer text-woot-500 dark:text-woot-500"
@click.capture="goBack"
>
<fluent-icon icon="chevron-left" />
@@ -2,7 +2,7 @@
<div class="empty-state py-16 px-1 ml-0 mr-0">
<h3
v-if="title"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-4xl font-thin"
class="text-slate-700 dark:text-slate-200 block text-center w-full text-xl font-thin"
>
{{ title }}
</h3>
@@ -15,6 +15,7 @@
<slot />
</div>
</template>
<script>
export default {
props: {
@@ -1,6 +1,6 @@
<template>
<div
class="inbox--name inline-flex items-center py-0.5 px-0 leading-3 font-medium bg-none text-slate-600 dark:text-slate-500 text-xs my-0 mx-2.5"
class="inbox--name inline-flex items-center py-0.5 px-0 leading-3 whitespace-nowrap font-medium bg-none text-slate-600 dark:text-slate-500 text-xs my-0 mx-2.5"
>
<fluent-icon
class="mr-0.5 rtl:ml-0.5 rtl:mr-0"
@@ -1,9 +1,11 @@
<template>
<div class="flex items-center justify-center p-8">
<h6
class="block text-base text-center w-100 text-slate-800 dark:text-slate-300"
class="flex items-center gap-2 text-base text-center w-100 text-slate-800 dark:text-slate-300"
>
<span class="mr-3">{{ message }}</span>
<span class="text-base font-medium text-slate-800 dark:text-slate-100">
{{ message }}
</span>
<span class="spinner" />
</h6>
</div>
@@ -1,57 +0,0 @@
<template>
<div
class="small-2 report-card"
:class="{ active: selected }"
@click="onClick(index)"
>
<h3 class="heading">
<span>{{ heading }}</span>
<fluent-icon
v-if="infoText"
v-tooltip="infoText"
size="14"
icon="info"
class="info-icon"
/>
</h3>
<div class="metric-wrap">
<h4 class="metric">
{{ point }}
</h4>
<span v-if="trend !== 0" :class="trendClass">{{ trendValue }}</span>
</div>
<p class="desc">
{{ desc }}
</p>
</div>
</template>
<script>
export default {
props: {
heading: { type: String, default: '' },
infoText: { type: String, default: '' },
point: { type: [Number, String], default: '' },
trend: { type: Number, default: null },
index: { type: Number, default: null },
desc: { type: String, default: '' },
selected: Boolean,
onClick: { type: Function, default: () => {} },
},
computed: {
trendClass() {
if (this.trend > 0) {
return 'metric-trend metric-up';
}
return 'metric-trend metric-down';
},
trendValue() {
if (this.trend > 0) {
return `+${this.trend}%`;
}
return `${this.trend}%`;
},
},
};
</script>
@@ -2,7 +2,7 @@
<div
class="bg-slate-25 dark:bg-slate-900 pt-4 pb-0 px-8 border-b border-solid border-slate-50 dark:border-slate-800/50"
>
<h2 class="text-2xl text-slate-800 dark:text-slate-100">
<h2 class="text-2xl text-slate-800 dark:text-slate-100 mb-1 font-medium">
{{ headerTitle }}
</h2>
<p
@@ -1,7 +1,7 @@
<template>
<footer
v-if="isFooterVisible"
class="bg-white dark:bg-slate-800 h-[60px] border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between py-0 px-4"
class="bg-white dark:bg-slate-800 h-12 border-t border-solid border-slate-75 dark:border-slate-700/50 flex items-center justify-between px-6"
>
<div class="left-aligned-wrap">
<div class="text-xs text-slate-600 dark:text-slate-200">
@@ -98,9 +98,7 @@ export default {
},
computed: {
pageFooterIconClass() {
return this.isRTLView
? 'margin-right-minus-slab'
: 'margin-left-minus-slab';
return this.isRTLView ? '-mr-3' : '-ml-3';
},
isFooterVisible() {
return this.totalCount && !(this.firstIndex > this.totalCount);
@@ -9,6 +9,7 @@
<img
v-show="shouldShowImage"
:src="src"
draggable="false"
:class="thumbnailClass"
@load="onImgLoad"
@error="onImgError"
@@ -225,8 +225,11 @@ export default {
<style lang="scss">
.audio-wave-wrapper {
min-height: 5rem;
height: 5rem;
@apply h-20 min-h-[5rem];
.video-js {
@apply bg-transparent max-h-60 min-h-[3rem] pt-4 px-0 pb-0 resize-none;
}
}
.video-js .vjs-control-bar {
background-color: transparent;
@@ -753,7 +753,7 @@ export default {
}
.ProseMirror-prompt {
@apply z-50 bg-slate-25 dark:bg-slate-700 rounded-md border border-solid border-slate-75 dark:border-slate-800;
@apply z-[9999] bg-slate-25 dark:bg-slate-700 rounded-md border border-solid border-slate-75 dark:border-slate-800 shadow-lg;
h5 {
@apply dark:text-slate-25 text-slate-800;
@@ -104,7 +104,7 @@
class="fixed top-0 bottom-0 left-0 right-0 z-20 flex flex-col items-center justify-center w-full h-full gap-2 text-slate-900 dark:text-slate-50 bg-modal-backdrop-light dark:bg-modal-backdrop-dark"
>
<fluent-icon icon="cloud-backup" size="40" />
<h4 class="page-sub-title text-slate-900 dark:text-slate-50">
<h4 class="text-2xl break-words text-slate-900 dark:text-slate-50">
{{ $t('CONVERSATION.REPLYBOX.DRAG_DROP') }}
</h4>
</div>
@@ -146,6 +146,6 @@ export default {
}
}
.button--note {
@apply text-yellow-600 dark:text-yellow-600;
@apply text-yellow-600 dark:text-yellow-600 bg-transparent dark:bg-transparent;
}
</style>
@@ -8,7 +8,7 @@
:chat="currentChat"
:is-inbox-view="isInboxView"
:is-contact-panel-open="isContactPanelOpen"
:show-back-button="isOnExpandedLayout"
:show-back-button="isOnExpandedLayout && !isInboxView"
@contact-panel-toggle="onToggleContactPanel"
/>
<woot-tabs
@@ -35,7 +35,10 @@
:is-contact-panel-open="isContactPanelOpen"
@contact-panel-toggle="onToggleContactPanel"
/>
<empty-state v-else :is-on-expanded-layout="isOnExpandedLayout" />
<empty-state
v-if="!currentChat.id && !isInboxView"
:is-on-expanded-layout="isOnExpandedLayout"
/>
<div
v-show="showContactPanel"
class="conversation-sidebar-wrap basis-full sm:basis-[17.5rem] md:basis-[18.75rem] lg:basis-[19.375rem] xl:basis-[20.625rem] 2xl:basis-[25rem] rtl:border-r border-slate-50 dark:border-slate-700 h-auto overflow-auto z-10 flex-shrink-0 flex-grow-0"
@@ -2,7 +2,8 @@
<div
class="relative flex items-start flex-grow-0 flex-shrink-0 w-auto max-w-full px-4 py-0 border-t-0 border-b-0 border-l-2 border-r-0 border-transparent border-solid cursor-pointer conversation hover:bg-slate-25 dark:hover:bg-slate-800 group"
:class="{
'active bg-slate-25 dark:bg-slate-800 border-woot-500': isActiveChat,
'animate-card-select bg-slate-25 dark:bg-slate-800 border-woot-500':
isActiveChat,
'unread-chat': hasUnread,
'has-inbox-name': showInboxName,
'conversation-selected': selected,
@@ -25,13 +26,12 @@
v-if="bulkActionCheck"
:src="currentContact.thumbnail"
:badge="inboxBadge"
class="columns"
:username="currentContact.name"
:status="currentContact.availability_status"
size="40px"
/>
<div
class="px-0 py-3 border-b group-hover:border-transparent border-slate-50 dark:border-slate-800/75 columns"
class="px-0 py-3 border-b group-hover:border-transparent flex-1 border-slate-50 dark:border-slate-800/75 w-[calc(100%-40px)]"
>
<div class="flex justify-between">
<inbox-name v-if="showInboxName" :inbox="inbox" />
@@ -51,7 +51,7 @@
</div>
</div>
<h4
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis overflow-hidden whitespace-nowrap w-[60%] text-slate-900 dark:text-slate-100"
class="conversation--user text-sm my-0 mx-2 capitalize pt-0.5 text-ellipsis font-medium overflow-hidden whitespace-nowrap w-[calc(100%-70px)] text-slate-900 dark:text-slate-100"
>
{{ currentContact.name }}
</h4>
@@ -3,11 +3,10 @@
class="bg-white dark:bg-slate-900 flex justify-between items-center py-2 px-4 border-b border-slate-50 dark:border-slate-800/50 flex-col md:flex-row"
>
<div
class="flex-1 w-full min-w-0 flex flex-col md:flex-row items-center justify-center"
class="flex-1 w-full min-w-0 flex flex-col items-center justify-center"
:class="isInboxView ? 'sm:flex-row' : 'md:flex-row'"
>
<div
class="flex justify-start items-center mr-4 rtl:mr-0 rtl:ml-4 min-w-0 w-[inherit]"
>
<div class="flex justify-start items-center min-w-0 w-fit max-w-full">
<back-button
v-if="showBackButton"
:back-url="backButtonUrl"
@@ -20,9 +19,11 @@
:status="currentContact.availability_status"
/>
<div
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-[inherit] overflow-hidden"
class="items-start flex flex-col ml-2 rtl:ml-0 rtl:mr-2 min-w-0 w-fit overflow-hidden"
>
<div class="flex items-center flex-row gap-1 m-0 p-0 w-[inherit]">
<div
class="flex items-center flex-row gap-1 m-0 p-0 w-fit max-w-full"
>
<woot-button
variant="link"
color-scheme="secondary"
@@ -30,7 +31,7 @@
@click.prevent="$emit('contact-panel-toggle')"
>
<span
class="text-base leading-tight text-slate-900 dark:text-slate-100"
class="text-base leading-tight font-medium text-slate-900 dark:text-slate-100"
>
{{ currentContact.name }}
</span>
@@ -66,7 +67,7 @@
</div>
</div>
<div
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0 rtl:relative rtl:left-6"
class="header-actions-wrap items-center flex flex-row flex-grow justify-end mt-3 lg:mt-0"
:class="{ 'justify-end': isContactPanelOpen }"
>
<more-actions :conversation-id="currentChat.id" />
@@ -87,7 +88,6 @@ import Thumbnail from '../Thumbnail.vue';
import wootConstants from 'dashboard/constants/globals';
import { conversationListPageURL } from 'dashboard/helper/URLHelper';
import { snoozedReopenTime } from 'dashboard/helper/snoozeHelpers';
import { frontendURL } from 'dashboard/helper/URLHelper';
export default {
components: {
@@ -128,9 +128,6 @@ export default {
params: { accountId, inbox_id: inboxId, label, teamId },
name,
} = this.$route;
if (this.isInboxView) {
return frontendURL(`accounts/${accountId}/inbox`);
}
return conversationListPageURL({
accountId,
inboxId,
@@ -8,7 +8,10 @@
/>
<form class="w-full" @submit.prevent="onSubmit">
<div class="w-full">
<div v-if="currentChat.meta.sender && currentChat.meta.sender.email">
<div
v-if="currentChat.meta.sender && currentChat.meta.sender.email"
class="flex items-center gap-2"
>
<input
id="contact"
v-model="selectedType"
@@ -20,7 +23,7 @@
$t('EMAIL_TRANSCRIPT.FORM.SEND_TO_CONTACT')
}}</label>
</div>
<div v-if="currentChat.meta.assignee">
<div v-if="currentChat.meta.assignee" class="flex items-center gap-2">
<input
id="assignee"
v-model="selectedType"
@@ -32,7 +35,7 @@
$t('EMAIL_TRANSCRIPT.FORM.SEND_TO_AGENT')
}}</label>
</div>
<div>
<div class="flex items-center gap-2">
<input
id="other_email_address"
v-model="selectedType"
@@ -577,7 +577,8 @@ export default {
> img,
> video {
@apply rounded-lg;
/** ensure that the bubble radius and image radius match*/
@apply rounded-[0.4rem];
}
> video {
@@ -600,8 +601,8 @@ export default {
@apply text-woot-400 dark:text-woot-400;
}
.text-block-title {
@apply text-slate-700 dark:text-slate-700;
.attachment-name {
@apply text-slate-700 dark:text-slate-200;
}
.download.button {
@@ -76,11 +76,16 @@
class="conversation-footer"
:class="{ 'modal-mask': isPopoutReplyBox }"
>
<div v-if="isAnyoneTyping" class="typing-indicator-wrap">
<div class="typing-indicator">
<div
v-if="isAnyoneTyping"
class="items-center flex h-0 absolute w-full -top-7"
>
<div
class="flex py-2 pr-4 pl-5 shadow-md rounded-full bg-white dark:bg-slate-700 text-slate-800 dark:text-slate-100 text-xs font-semibold my-2.5 mx-auto"
>
{{ typingUserNames }}
<img
class="gif"
class="ltr:ml-2 rtl:mr-2 w-6"
src="~dashboard/assets/images/typing.gif"
alt="Someone is typing"
/>
@@ -547,6 +552,8 @@ export default {
<style scoped lang="scss">
.modal-mask {
@apply absolute;
&::v-deep {
.ProseMirror-woot-style {
@apply max-h-[25rem];
@@ -1,9 +1,9 @@
<template>
<div class="columns onboarding-wrap">
<div class="onboarding-wrap">
<div class="onboarding">
<div class="scroll-wrap">
<div class="features-item">
<h1 class="page-title">
<h1 class="text-2xl text-slate-900 dark:text-slate-50">
<span>{{
$t('ONBOARDING.TITLE', {
installationName: globalConfig.installationName,
@@ -30,7 +30,7 @@
</p>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<span class="emoji">💬</span>
<span class="conversation--title">{{
$t('ONBOARDING.ALL_CONVERSATION.TITLE')
@@ -41,7 +41,7 @@
</p>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<span class="emoji">👥</span>
{{ $t('ONBOARDING.TEAM_MEMBERS.TITLE') }}
</h2>
@@ -53,7 +53,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<span class="emoji">📥</span>{{ $t('ONBOARDING.INBOXES.TITLE') }}
</h2>
<p class="intro-body">
@@ -64,7 +64,7 @@
</router-link>
</div>
<div class="features-item">
<h2 class="block-title text-black-900 dark:text-slate-200">
<h2 class="text-lg text-black-900 dark:text-slate-200">
<span class="emoji">🔖</span>{{ $t('ONBOARDING.LABELS.TITLE') }}
</h2>
<p class="intro-body">
@@ -1202,10 +1202,28 @@ export default {
}
.reply-box {
transition:
box-shadow 0.35s cubic-bezier(0.37, 0, 0.63, 1),
height 2s cubic-bezier(0.37, 0, 0.63, 1);
@apply relative border-t border-slate-50 dark:border-slate-700 bg-white dark:bg-slate-900;
&.is-focused {
box-shadow:
0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
&.is-private {
@apply bg-yellow-50 dark:bg-yellow-200;
@apply bg-yellow-100 dark:bg-yellow-800;
.reply-box__top {
@apply bg-yellow-100 dark:bg-yellow-800;
> input {
@apply bg-yellow-100 dark:bg-yellow-800;
}
}
}
}
.send-button {
@@ -1214,6 +1232,10 @@ export default {
.reply-box__top {
@apply relative py-0 px-4 -mt-px border-t border-solid border-slate-50 dark:border-slate-700;
textarea {
@apply shadow-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
}
}
.emoji-dialog {
@@ -5,10 +5,11 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.TO') }}
</label>
<div class="input-group-field">
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<woot-input
v-model.trim="$v.toEmailsVal.$model"
type="text"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
:class="{ error: $v.toEmailsVal.$error }"
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
@blur="onBlur"
@@ -21,9 +22,10 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.LABEL') }}
</label>
<div class="input-group-field">
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<woot-input
v-model.trim="$v.ccEmailsVal.$model"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
type="text"
:class="{ error: $v.ccEmailsVal.$error }"
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
@@ -48,10 +50,11 @@
<label class="input-group-label">
{{ $t('CONVERSATION.REPLYBOX.EMAIL_HEAD.BCC.LABEL') }}
</label>
<div class="input-group-field">
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
<woot-input
v-model.trim="$v.bccEmailsVal.$model"
type="text"
class="[&>input]:mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
:class="{ error: $v.bccEmailsVal.$error }"
:placeholder="
$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.BCC.PLACEHOLDER')
@@ -150,14 +153,11 @@ export default {
@apply text-sm text-red-500 dark:text-red-500;
}
.input-group {
@apply border-b border-solid border-slate-75 dark:border-slate-700 my-1;
@apply border-b border-solid border-slate-75 dark:border-slate-700 my-1 flex items-center gap-2;
.input-group-label {
@apply border-transparent bg-transparent text-xs font-semibold pl-0;
}
.input-group-field::v-deep input {
@apply mb-0 border-transparent;
}
}
.input-group.error {
@@ -1,6 +1,6 @@
<template>
<div class="w-full">
<div class="templates__list-search">
<div class="templates__list-search gap-1">
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
@@ -17,7 +17,7 @@
@click="$emit('onSelect', template)"
>
<div>
<div class="flex-between">
<div class="flex items-center justify-between mb-2.5">
<p class="label-title">
{{ template.name }}
</p>
@@ -97,10 +97,6 @@ export default {
</script>
<style scoped lang="scss">
.flex-between {
@apply flex justify-between mb-2.5;
}
.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;
@@ -109,7 +105,7 @@ export default {
}
.templates__search-input {
@apply bg-transparent border-0 text-xs h-auto m-0;
@apply bg-transparent border-0 text-xs h-9 m-0;
}
}
.template__list-container {
@@ -4,7 +4,7 @@
<fluent-icon icon="document" class="file--icon" size="32" />
</div>
<div class="meta">
<h5 class="text-block-title">
<h5 class="attachment-name text-slate-700 dark:text-slate-400">
{{ decodeURI(fileName) }}
</h5>
<a
@@ -62,7 +62,7 @@ export default {
margin-right: $space-slab;
}
.text-block-title {
.attachment-name {
margin: 0;
color: $color-white;
font-weight: $font-weight-bold;
@@ -2,20 +2,23 @@
<div class="message-text__wrap" :class="attachmentTypeClasses">
<img
v-if="isImage && !isImageError"
:src="attachment.data_url"
class="bg-woot-200 dark:bg-woot-900"
:src="dataUrl"
:width="imageWidth"
:height="imageHeight"
@click="onClick"
@error="onImgError"
/>
<video
v-if="isVideo"
:src="attachment.data_url"
:src="dataUrl"
muted
playsInline
@error="onImgError"
@click="onClick"
/>
<audio v-else-if="isAudio" controls class="skip-context-menu">
<source :src="`${attachment.data_url}?t=${Date.now()}`" />
<source :src="`${dataUrl}?t=${Date.now()}`" />
</audio>
<gallery-view
v-if="show"
@@ -31,7 +34,6 @@
<script>
import { mapGetters } from 'vuex';
import { hasPressedCommand } from 'shared/helpers/KeyboardHelpers';
import GalleryView from '../components/GalleryView.vue';
const ALLOWED_FILE_TYPES = {
@@ -81,6 +83,15 @@ export default {
);
return attachments;
},
dataUrl() {
return this.attachment.data_url;
},
imageWidth() {
return this.attachment.width ? `${this.attachment.width}px` : 'auto';
},
imageHeight() {
return this.attachment.height ? `${this.attachment.height}px` : 'auto';
},
},
watch: {
attachment() {
@@ -1,17 +1,45 @@
<script setup>
import { computed } from 'vue';
const props = defineProps({
latitude: {
type: Number,
default: undefined,
},
longitude: {
type: Number,
default: undefined,
},
name: {
type: String,
default: '',
},
});
const mapUrl = computed(
() => `https://maps.google.com/?q=${props.latitude},${props.longitude}`
);
</script>
<template>
<div class="location message-text__wrap">
<div class="icon-wrap">
<fluent-icon icon="location" class="file--icon" size="32" />
</div>
<div class="meta">
<div
class="flex flex-row items-center justify-start gap-1 w-full py-1 px-0 cursor-pointer overflow-hidden"
>
<fluent-icon
icon="location"
class="text-slate-600 dark:text-slate-200 leading-none my-0 flex items-center flex-shrink-0"
size="32"
/>
<div class="flex flex-col items-start flex-1 min-w-0">
<h5
class="text-block-title overflow-hidden whitespace-nowrap text-ellipsis"
class="text-sm text-slate-800 dark:text-slate-100 truncate m-0 w-full"
:title="name"
>
{{ name }}
</h5>
<div class="link-wrap">
<div class="flex items-center">
<a
class="download clear link button small"
class="text-woot-600 dark:text-woot-600 text-xs underline"
rel="noreferrer noopener nofollow"
target="_blank"
:href="mapUrl"
@@ -22,49 +50,3 @@
</div>
</div>
</template>
<script>
export default {
props: {
latitude: {
type: Number,
default: undefined,
},
longitude: {
type: Number,
default: undefined,
},
name: {
type: String,
default: '',
},
},
computed: {
mapUrl() {
return `https://maps.google.com/?q=${this.latitude},${this.longitude}`;
},
},
};
</script>
<style lang="scss" scoped>
.location {
@apply flex flex-row py-1 px-0 cursor-pointer;
.icon-wrap {
@apply text-slate-600 dark:text-slate-200 leading-none my-0 mx-1;
}
.text-block-title {
@apply m-0 text-slate-800 dark:text-slate-100 break-words;
}
.meta {
@apply flex flex-col items-center pr-4;
}
.link-wrap {
@apply flex;
}
}
</style>
@@ -7,11 +7,12 @@
}"
>
<div v-if="!isEmail" v-dompurify-html="message" class="text-content" />
<letter
v-else
class="text-content bg-white dark:bg-white text-slate-900 dark:text-slate-900 p-2 rounded-[4px]"
:html="message"
/>
<div v-else @click="handleClickOnContent">
<letter
class="text-content bg-white dark:bg-white text-slate-900 dark:text-slate-900 p-2 rounded-[4px]"
:html="message"
/>
</div>
<button
v-if="showQuoteToggle"
class="text-slate-300 dark:text-slate-300 cursor-pointer text-xs py-1"
@@ -26,14 +27,23 @@
{{ $t('CHAT_LIST.SHOW_QUOTED_TEXT') }}
</span>
</button>
<gallery-view
v-if="showGalleryViewer"
:show.sync="showGalleryViewer"
:attachment="attachment"
:all-attachments="availableAttachments"
@error="onClose"
@close="onClose"
/>
</div>
</template>
<script>
import Letter from 'vue-letter';
import GalleryView from '../components/GalleryView.vue';
export default {
components: { Letter },
components: { Letter, GalleryView },
props: {
message: {
type: String,
@@ -51,6 +61,9 @@ export default {
data() {
return {
showQuotedContent: false,
showGalleryViewer: false,
attachment: {},
availableAttachments: [],
};
},
computed: {
@@ -71,6 +84,33 @@ export default {
toggleQuotedContent() {
this.showQuotedContent = !this.showQuotedContent;
},
handleClickOnContent(event) {
// if event target is IMG and not close in A tag
// then open image preview
const isImageElement = event.target.tagName === 'IMG';
const isWrappedInLink = event.target.closest('A');
if (isImageElement && !isWrappedInLink) {
this.openImagePreview(event.target.src);
}
},
openImagePreview(src) {
this.showGalleryViewer = true;
this.attachment = {
file_type: 'image',
data_url: src,
message_id: Math.floor(Math.random() * 100),
};
this.availableAttachments = [{ ...this.attachment }];
},
onClose() {
this.showGalleryViewer = false;
this.resetAttachmentData();
},
resetAttachmentData() {
this.attachment = {};
this.availableAttachments = [];
},
},
};
</script>
@@ -82,6 +122,7 @@ export default {
ol {
padding-left: var(--space-two);
}
table {
margin: 0;
border: 0;
@@ -5,11 +5,11 @@
show
:on-close="onClose"
>
<div class="column content">
<div class="content">
<p>
<b>{{ $t('TRANSLATE_MODAL.ORIGINAL_CONTENT') }}</b>
</p>
<p v-dompurify-html="content" />
<p v-dompurify-html="content" class="mb-0" />
<br />
<hr />
<div v-if="translationsAvailable">
@@ -8,47 +8,66 @@
>
<div
v-on-clickaway="onClose"
class="bg-white dark:bg-slate-900 flex flex-col h-[inherit] w-[inherit]"
class="bg-white dark:bg-slate-900 flex flex-col h-[inherit] w-[inherit] overflow-hidden"
@click="onClose"
>
<div class="items-center flex h-16 justify-between py-2 px-6 w-full">
<div class="items-center flex justify-start min-w-[15rem]" @click.stop>
<div
class="bg-white dark:bg-slate-900 z-10 flex items-center justify-between w-full h-16 px-6 py-2"
@click.stop
>
<div
v-if="senderDetails"
class="items-center flex justify-start min-w-[15rem]"
>
<thumbnail
v-if="senderDetails.avatar"
:username="senderDetails.name"
:src="senderDetails.avatar"
/>
<div
class="flex items-start flex-col justify-center ml-2 rtl:ml-0 rtl:mr-2"
class="flex flex-col items-start justify-center ml-2 rtl:ml-0 rtl:mr-2"
>
<h3
class="sub-block-title inline-block leading-[1.4] m-0 p-0 capitalize"
>
<h3 class="text-base inline-block leading-[1.4] m-0 p-0 capitalize">
<span
class="text-slate-800 dark:text-slate-100 overflow-hidden whitespace-nowrap text-ellipsis"
class="overflow-hidden text-slate-800 dark:text-slate-100 whitespace-nowrap text-ellipsis"
>
{{ senderDetails.name }}
</span>
</h3>
<span
class="text-xs m-0 p-0 text-slate-400 dark:text-slate-200 overflow-hidden whitespace-nowrap text-ellipsis"
class="p-0 m-0 overflow-hidden text-xs text-slate-400 dark:text-slate-200 whitespace-nowrap text-ellipsis"
>
{{ readableTime }}
</span>
</div>
</div>
<div
class="items-center text-slate-700 dark:text-slate-100 flex font-semibold justify-start min-w-0 p-1 w-auto text-block-title"
class="flex items-center justify-start w-auto min-w-0 p-1 text-sm font-semibold text-slate-700 dark:text-slate-100"
>
<span
class="text-slate-700 dark:text-slate-100 overflow-hidden whitespace-nowrap text-ellipsis"
>
{{ fileNameFromDataUrl }}
</span>
v-dompurify-html="fileNameFromDataUrl"
class="overflow-hidden text-slate-700 dark:text-slate-100 whitespace-nowrap text-ellipsis"
/>
</div>
<div
class="items-center flex gap-2 justify-end min-w-[8rem] sm:min-w-[15rem]"
@click.stop
>
<woot-button
v-if="isImage"
size="large"
color-scheme="secondary"
variant="clear"
icon="zoom-in"
@click="onZoom(0.1)"
/>
<woot-button
v-if="isImage"
size="large"
color-scheme="secondary"
variant="clear"
icon="zoom-out"
@click="onZoom(-0.1)"
/>
<woot-button
v-if="isImage"
size="large"
@@ -81,10 +100,11 @@
/>
</div>
</div>
<div class="items-center flex h-full justify-center w-full">
<div class="flex items-center justify-center w-full h-full">
<div class="flex justify-center min-w-[6.25rem] w-[6.25rem]">
<woot-button
v-if="hasMoreThanOneAttachment"
class="z-10"
size="large"
variant="smooth"
color-scheme="primary"
@@ -98,15 +118,16 @@
"
/>
</div>
<div class="flex items-center flex-col justify-center w-full h-full">
<div class="flex flex-col items-center justify-center w-full h-full">
<div>
<img
v-if="isImage"
:key="activeAttachment.message_id"
:src="activeAttachment.data_url"
class="modal-image skip-context-menu my-0 mx-auto"
class="mx-auto my-0 duration-150 ease-in-out transform modal-image skip-context-menu"
:style="imageRotationStyle"
@click.stop
@click.stop="onClickZoomImage"
@wheel.stop="onWheelImageZoom"
/>
<video
v-if="isVideo"
@@ -114,7 +135,7 @@
:src="activeAttachment.data_url"
controls
playsInline
class="modal-video skip-context-menu my-0 mx-auto"
class="mx-auto my-0 modal-video skip-context-menu"
@click.stop
/>
<audio
@@ -131,6 +152,7 @@
<div class="flex justify-center min-w-[6.25rem] w-[6.25rem]">
<woot-button
v-if="hasMoreThanOneAttachment"
class="z-10"
size="large"
variant="smooth"
color-scheme="primary"
@@ -145,9 +167,9 @@
/>
</div>
</div>
<div class="items-center flex h-16 justify-center w-full py-2 px-6">
<div class="flex items-center justify-center w-full h-16 px-6 py-2 z-10">
<div
class="items-center rounded-sm flex font-semibold justify-center min-w-[5rem] p-1 bg-slate-25 dark:bg-slate-800 text-slate-600 dark:text-slate-200 text-block-title"
class="items-center rounded-sm flex font-semibold justify-center min-w-[5rem] p-1 bg-slate-25 dark:bg-slate-800 text-slate-600 dark:text-slate-200 text-sm"
>
<span class="count">
{{ `${activeImageIndex + 1} / ${allAttachments.length}` }}
@@ -176,6 +198,9 @@ const ALLOWED_FILE_TYPES = {
AUDIO: 'audio',
};
const MAX_ZOOM_LEVEL = 2;
const MIN_ZOOM_LEVEL = 1;
export default {
components: {
Thumbnail,
@@ -197,6 +222,7 @@ export default {
},
data() {
return {
zoomScale: 1,
activeAttachment: {},
activeFileType: '',
activeImageIndex:
@@ -214,12 +240,9 @@ export default {
return this.allAttachments.length > 1;
},
readableTime() {
if (!this.activeAttachment.created_at) return '';
const time = this.messageTimestamp(
this.activeAttachment.created_at,
'LLL d yyyy, h:mm a'
);
return time || '';
const { created_at: createdAt } = this.activeAttachment;
if (!createdAt) return '';
return this.messageTimestamp(createdAt, 'LLL d yyyy, h:mm a') || '';
},
isImage() {
return this.activeFileType === ALLOWED_FILE_TYPES.IMAGE;
@@ -248,11 +271,12 @@ export default {
const { data_url: dataUrl } = this.activeAttachment;
if (!dataUrl) return '';
const fileName = dataUrl?.split('/').pop();
return fileName || '';
return decodeURIComponent(fileName || '');
},
imageRotationStyle() {
return {
transform: `rotate(${this.activeImageRotation}deg)`,
transform: `rotate(${this.activeImageRotation}deg) scale(${this.zoomScale})`,
cursor: this.zoomScale < MAX_ZOOM_LEVEL ? 'zoom-in' : 'zoom-out',
};
},
},
@@ -270,6 +294,7 @@ export default {
this.activeImageIndex = index;
this.setImageAndVideoSrc(attachment);
this.activeImageRotation = 0;
this.zoomScale = 1;
},
setImageAndVideoSrc(attachment) {
const { file_type: type } = attachment;
@@ -318,6 +343,37 @@ export default {
this.activeImageRotation += rotation;
}
},
onClickZoomImage() {
this.onZoom(0.1);
},
onZoom(scale) {
if (!this.isImage) {
return;
}
const newZoomScale = this.zoomScale + scale;
// Check if the new zoom scale is within the allowed range
if (newZoomScale > MAX_ZOOM_LEVEL) {
// Set zoom to max but do not reset to default
this.zoomScale = MAX_ZOOM_LEVEL;
return;
}
if (newZoomScale < MIN_ZOOM_LEVEL) {
// Set zoom to min but do not reset to default
this.zoomScale = MIN_ZOOM_LEVEL;
return;
}
// If within bounds, update the zoom scale
this.zoomScale = newZoomScale;
},
onWheelImageZoom(e) {
if (!this.isImage) {
return;
}
const scale = e.deltaY > 0 ? -0.1 : 0.1;
this.onZoom(scale);
},
},
};
</script>
@@ -0,0 +1,103 @@
<template>
<div
class="flex items-center px-2 truncate border min-w-fit border-slate-75 dark:border-slate-700"
:class="showExtendedInfo ? 'py-[5px] rounded-lg' : 'py-0.5 gap-1 rounded'"
>
<div
class="flex items-center gap-1"
:class="
showExtendedInfo &&
'ltr:pr-1.5 rtl:pl-1.5 ltr:border-r rtl:border-l border-solid border-slate-75 dark:border-slate-700'
"
>
<fluent-icon
size="14"
:icon="slaStatus.icon"
type="outline"
:icon-lib="isSlaMissed ? 'lucide' : 'fluent'"
class="flex-shrink-0"
:class="slaTextStyles"
/>
<span
v-if="showExtendedInfo"
class="text-xs font-medium"
:class="slaTextStyles"
>
{{ slaStatusText }}
</span>
</div>
<span
class="text-xs font-medium"
:class="[slaTextStyles, showExtendedInfo && 'ltr:pl-1.5 rtl:pr-1.5']"
>
{{ slaStatus.threshold }}
</span>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { evaluateSLAStatus } from '../helpers/SLAHelper';
// const REFRESH_INTERVAL = 60000;
export default {
props: {
chat: {
type: Object,
default: () => ({}),
},
showExtendedInfo: {
type: Boolean,
default: false,
},
},
data() {
return {
timer: null,
slaStatus: {},
};
},
computed: {
...mapGetters({
activeSLA: 'sla/getSLAById',
}),
slaPolicyId() {
return this.chat?.sla_policy_id;
},
sla() {
if (!this.slaPolicyId) return null;
return this.activeSLA(this.slaPolicyId);
},
isSlaMissed() {
return this.slaStatus?.isSlaMissed;
},
slaTextStyles() {
return this.isSlaMissed
? 'text-red-400 dark:text-red-300'
: 'text-yellow-600 dark:text-yellow-500';
},
slaStatusText() {
const upperCaseType = this.slaStatus?.type?.toUpperCase(); // FRT, NRT, or RT
const statusKey = this.isSlaMissed ? 'BREACH' : 'DUE';
return this.$t(`CONVERSATION.HEADER.SLA_STATUS.${upperCaseType}`, {
status: this.$t(`CONVERSATION.HEADER.SLA_STATUS.${statusKey}`),
});
},
},
watch: {
chat() {
this.updateSlaStatus();
},
},
mounted() {
this.updateSlaStatus();
},
methods: {
updateSlaStatus() {
this.slaStatus = evaluateSLAStatus(this.sla, this.chat);
},
},
};
</script>
@@ -1,5 +1,9 @@
<template>
<div class="menu" role="button" @click.stop="$emit('click')">
<div
class="menu text-slate-800 dark:text-slate-100"
role="button"
@click.stop="$emit('click')"
>
<fluent-icon
v-if="variant === 'icon' && option.icon"
:icon="option.icon"
@@ -48,6 +52,7 @@ export default {
.menu {
width: calc(var(--space-mega) * 2);
@apply flex items-center flex-nowrap p-1 rounded-sm overflow-hidden cursor-pointer;
.menu-label {
@apply my-0 mx-2 text-xs flex-shrink-0;
}
@@ -1,6 +1,6 @@
<template>
<div
class="menu-with-submenu min-width-calc w-full p-1 flex items-center h-7 rounded-md relative bg-white dark:bg-slate-700 justify-between hover:bg-woot-75 cursor-pointer dark:hover:bg-slate-800"
class="text-slate-800 dark:text-slate-100 menu-with-submenu min-width-calc w-full p-1 flex items-center h-7 rounded-md relative bg-white dark:bg-slate-700 justify-between hover:bg-woot-75 cursor-pointer dark:hover:bg-slate-800"
:class="!subMenuAvailable ? 'opacity-50 cursor-not-allowed' : ''"
>
<div class="flex items-center">
@@ -5,7 +5,7 @@
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
</svg>
</div>
<div class="header flex-between">
<div class="header flex items-center justify-between">
<span>{{ $t('BULK_ACTION.AGENT_SELECT_LABEL') }}</span>
<woot-button
size="tiny"
@@ -26,7 +26,9 @@
<div v-else class="agent__list-container">
<ul v-if="!selectedAgent">
<li class="search-container">
<div class="agent-list-search flex-between">
<div
class="agent-list-search h-8 flex justify-between items-center gap-2"
>
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
@@ -45,7 +47,9 @@
:username="agent.name"
size="22px"
/>
<span class="reports-option__title">{{ agent.name }}</span>
<span class="my-0 text-slate-800 dark:text-slate-75">
{{ agent.name }}
</span>
</div>
</li>
</ul>
@@ -205,7 +209,7 @@ export default {
}
.agent--search_input {
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset];
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset] w-full;
}
}
}
@@ -231,7 +235,7 @@ ul {
}
.agent-list-item {
@apply flex items-center p-2.5 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-900;
@apply flex items-center p-2.5 gap-2 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-900;
span {
@apply text-sm;
}
@@ -1,7 +1,7 @@
<template>
<div class="bulk-action__container">
<div class="flex-between">
<label class="bulk-action__panel flex-between">
<div class="flex items-center justify-between">
<label class="bulk-action__panel flex items-center justify-between">
<input
ref="selectAllCheck"
type="checkbox"
@@ -18,14 +18,13 @@
}}
</span>
</label>
<div class="bulk-action__actions flex-between">
<div class="bulk-action__actions flex gap-1 items-center">
<woot-button
v-tooltip="$t('BULK_ACTION.LABELS.ASSIGN_LABELS')"
size="tiny"
variant="smooth"
color-scheme="secondary"
icon="tag"
class="margin-right-smaller"
@click="toggleLabelActions"
/>
<woot-button
@@ -34,7 +33,6 @@
variant="smooth"
color-scheme="secondary"
icon="repeat"
class="margin-right-smaller"
@click="toggleUpdateActions"
/>
<woot-button
@@ -43,7 +41,6 @@
variant="smooth"
color-scheme="secondary"
icon="person-assign"
class="margin-right-smaller"
@click="toggleAgentList"
/>
<woot-button
@@ -5,7 +5,7 @@
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
</svg>
</div>
<div class="header flex-between">
<div class="header flex items-center justify-between">
<span>{{ $t('BULK_ACTION.LABELS.ASSIGN_LABELS') }}</span>
<woot-button
size="tiny"
@@ -17,7 +17,9 @@
</div>
<div class="labels-list">
<header class="labels-list__header">
<div class="label-list-search flex-between">
<div
class="label-list-search h-8 flex justify-between items-center gap-2"
>
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
@@ -138,7 +140,7 @@ export default {
}
.label--search_input {
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset];
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset] w-full;
}
}
@@ -5,7 +5,7 @@
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
</svg>
</div>
<div class="header flex-between">
<div class="header flex items-center justify-between">
<span>{{ $t('BULK_ACTION.TEAMS.TEAM_SELECT_LABEL') }}</span>
<woot-button
size="tiny"
@@ -19,7 +19,9 @@
<div class="team__list-container">
<ul>
<li class="search-container">
<div class="agent-list-search flex-between">
<div
class="agent-list-search h-8 flex justify-between items-center gap-2"
>
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
@@ -33,15 +35,21 @@
<template v-if="filteredTeams.length">
<li v-for="team in filteredTeams" :key="team.id">
<div class="team__list-item" @click="assignTeam(team)">
<span class="reports-option__title">{{ team.name }}</span>
<span
class="my-0 ltr:ml-2 rtl:mr-2 text-slate-800 dark:text-slate-75"
>
{{ team.name }}
</span>
</div>
</li>
</template>
<li v-else>
<div class="team__list-item">
<span class="reports-option__title">{{
$t('BULK_ACTION.TEAMS.NO_TEAMS_AVAILABLE')
}}</span>
<span
class="my-0 ltr:ml-2 rtl:mr-2 text-slate-800 dark:text-slate-75"
>
{{ $t('BULK_ACTION.TEAMS.NO_TEAMS_AVAILABLE') }}
</span>
</div>
</li>
</ul>
@@ -106,7 +114,7 @@ export default {
}
.agent--search_input {
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent h-[unset];
@apply border-0 text-xs m-0 dark:bg-transparent bg-transparent w-full h-[unset];
}
}
}
@@ -5,7 +5,7 @@
<path d="M20 12l-8-8-12 12" fill-rule="evenodd" stroke-width="1px" />
</svg>
</div>
<div class="header flex-between">
<div class="header flex items-center justify-between">
<span>{{ $t('BULK_ACTION.UPDATE.CHANGE_STATUS') }}</span>
<woot-button
size="tiny"

Some files were not shown because too many files have changed in this diff Show More