mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Vite + vue 3 💚 (#10047)
Fixes https://github.com/chatwoot/chatwoot/issues/8436 Fixes https://github.com/chatwoot/chatwoot/issues/9767 Fixes https://github.com/chatwoot/chatwoot/issues/10156 Fixes https://github.com/chatwoot/chatwoot/issues/6031 Fixes https://github.com/chatwoot/chatwoot/issues/5696 Fixes https://github.com/chatwoot/chatwoot/issues/9250 Fixes https://github.com/chatwoot/chatwoot/issues/9762 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Black.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Black.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-BlackItalic.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Bold.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Bold.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-BoldItalic.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ExtraBold.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ExtraBold.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ExtraLight.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ExtraLight.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Italic.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Italic.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Light.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Light.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-LightItalic.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-LightItalic.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Medium.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Medium.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Regular.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Regular.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-SemiBold.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-SemiBold.woff2
Normal file
Binary file not shown.
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Thin.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-Thin.woff2
Normal file
Binary file not shown.
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ThinItalic.woff2
Normal file
BIN
app/javascript/shared/assets/fonts/Inter/Inter-ThinItalic.woff2
Normal file
Binary file not shown.
@@ -3,7 +3,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Thin.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Thin.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -11,7 +11,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-ExtraLight.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-ExtraLight.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -19,7 +19,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Light.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Light.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -27,7 +27,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Regular.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -35,7 +35,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Medium.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -43,7 +43,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-SemiBold.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-SemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -51,7 +51,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Bold.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -59,7 +59,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-ExtraBold.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-ExtraBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -67,5 +67,5 @@
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/InterDisplay/InterDisplay-Black.woff2') format('woff2');
|
||||
src: url('shared/assets/fonts/InterDisplay/InterDisplay-Black.woff2') format('woff2');
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,8 +3,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Thin.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Thin.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Thin.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -12,8 +11,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Light.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Light.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Light.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -21,8 +19,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Regular.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Regular.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -30,8 +27,7 @@
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Italic.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Italic.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Italic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -39,8 +35,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Medium.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Medium.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -48,8 +43,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-SemiBold.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-SemiBold.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-SemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -57,6 +51,5 @@
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Bold.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Bold.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-ExtraLight.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-Light.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-Italic.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-SemiBold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-Bold.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'PlusJakarta';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/PlusJakartaSans/PlusJakartaSans-ExtraBold.woff2') format('woff2');
|
||||
}
|
||||
@@ -3,8 +3,7 @@
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Regular.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Regular.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -12,6 +11,5 @@
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('~shared/assets/fonts/Inter-Medium.woff2') format('woff2'),
|
||||
url('~shared/assets/fonts/Inter-Medium.woff') format('woff');
|
||||
src: url('shared/assets/fonts/Inter/Inter-Medium.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
@import "~ionicons/scss/ionicons-variables";
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: $ionicons-font-family;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url("#{$ionicons-font-path}/ionicons.woff?v=#{$ionicons-version}") format("woff");
|
||||
}
|
||||
|
||||
.ion {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: inline-block;
|
||||
font-family: $ionicons-font-family;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
text-rendering: auto;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
@import "~ionicons/scss/ionicons-icons";
|
||||
@@ -69,7 +69,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.branding--image {
|
||||
margin-right: $space-smaller;
|
||||
|
||||
@@ -48,21 +48,11 @@ export default {
|
||||
return styles;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onClick(e) {
|
||||
this.$emit('click', e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
:class="buttonClassName"
|
||||
:style="buttonStyles"
|
||||
:disabled="disabled"
|
||||
@click="onClick"
|
||||
>
|
||||
<button :class="buttonClassName" :style="buttonStyles" :disabled="disabled">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
@@ -56,8 +56,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~dashboard/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.action-button {
|
||||
align-items: center;
|
||||
|
||||
@@ -56,8 +56,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~dashboard/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
@import 'dashboard/assets/scss/mixins.scss';
|
||||
|
||||
.card-message {
|
||||
max-width: 220px;
|
||||
|
||||
@@ -167,7 +167,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.form {
|
||||
padding: $space-normal;
|
||||
|
||||
@@ -20,7 +20,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click', this.action);
|
||||
this.$emit('optionSelect', this.action);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.option {
|
||||
border-radius: $space-jumbo;
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
return this.selected === option.id;
|
||||
},
|
||||
onClick(selectedOption) {
|
||||
this.$emit('click', selectedOption);
|
||||
this.$emit('optionSelect', selectedOption);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
:key="option.id"
|
||||
:action="option"
|
||||
:is-selected="isSelected(option)"
|
||||
@click="onClick"
|
||||
@option-select="onClick"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -68,7 +68,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~dashboard/assets/scss/variables.scss';
|
||||
@import 'dashboard/assets/scss/variables.scss';
|
||||
|
||||
.has-selected {
|
||||
.option-button:not(.is-selected) {
|
||||
color: $color-light-gray;
|
||||
@@ -78,7 +79,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
.options-message {
|
||||
max-width: 17rem;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import CustomerSatisfaction from './CustomerSatisfaction';
|
||||
|
||||
export default {
|
||||
title: 'Components/CustomerSatisfaction',
|
||||
component: CustomerSatisfaction,
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { CustomerSatisfaction },
|
||||
template: '<customer-satisfaction />',
|
||||
});
|
||||
|
||||
export const Item = Template.bind({});
|
||||
Item.args = {
|
||||
onClick: action('Selected'),
|
||||
};
|
||||
@@ -156,8 +156,8 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables.scss';
|
||||
@import '~widget/assets/scss/mixins.scss';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
@import 'widget/assets/scss/mixins.scss';
|
||||
|
||||
.customer-satisfaction {
|
||||
@include light-shadow;
|
||||
@@ -204,6 +204,7 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feedback-form {
|
||||
display: flex;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~widget/assets/scss/variables';
|
||||
@import 'widget/assets/scss/variables';
|
||||
|
||||
.date--separator {
|
||||
font-size: $font-size-default;
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
if (this.value) {
|
||||
if (this.modelValue) {
|
||||
this.resizeTextarea();
|
||||
this.setCursor();
|
||||
} else {
|
||||
@@ -93,7 +93,7 @@ export default {
|
||||
methods: {
|
||||
resizeTextarea() {
|
||||
this.$el.style.height = 'auto';
|
||||
if (!this.value) {
|
||||
if (!this.modelValue) {
|
||||
this.$el.style.height = `${this.minHeight}rem`;
|
||||
} else {
|
||||
this.$el.style.height = `${this.$el.scrollHeight}px`;
|
||||
@@ -104,9 +104,10 @@ export default {
|
||||
// is supposed to be added, else we remove it.
|
||||
toggleSignatureInEditor(signatureEnabled) {
|
||||
const valueWithSignature = signatureEnabled
|
||||
? appendSignature(this.value, this.cleanedSignature)
|
||||
: removeSignature(this.value, this.cleanedSignature);
|
||||
? appendSignature(this.modelValue, this.cleanedSignature)
|
||||
: removeSignature(this.modelValue, this.cleanedSignature);
|
||||
|
||||
this.$emit('update:modelValue', valueWithSignature);
|
||||
this.$emit('input', valueWithSignature);
|
||||
|
||||
this.$nextTick(() => {
|
||||
@@ -116,7 +117,7 @@ export default {
|
||||
},
|
||||
setCursor() {
|
||||
const bodyWithoutSignature = removeSignature(
|
||||
this.value,
|
||||
this.modelValue,
|
||||
this.cleanedSignature
|
||||
);
|
||||
|
||||
@@ -130,6 +131,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onInput(event) {
|
||||
this.$emit('update:modelValue', event.target.value);
|
||||
this.$emit('input', event.target.value);
|
||||
this.resizeTextarea();
|
||||
},
|
||||
@@ -155,7 +157,7 @@ export default {
|
||||
ref="textarea"
|
||||
:placeholder="placeholder"
|
||||
:rows="rows"
|
||||
:value="value"
|
||||
:value="modelValue"
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@keyup="onKeyup"
|
||||
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '~widget/assets/scss/variables';
|
||||
@import 'widget/assets/scss/variables.scss';
|
||||
|
||||
@mixin color-spinner() {
|
||||
@keyframes spinner {
|
||||
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
value: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
@@ -18,9 +18,14 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onChange(event) {
|
||||
this.$emit('input', event.target.value);
|
||||
computed: {
|
||||
computedModel: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit('update:modelValue', value);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -39,6 +44,7 @@ export default {
|
||||
{{ label }}
|
||||
</div>
|
||||
<textarea
|
||||
v-model="computedModel"
|
||||
class="w-full px-3 py-2 leading-tight border rounded outline-none resize-none text-slate-700"
|
||||
:class="{
|
||||
'border-black-200 hover:border-black-300 focus:border-black-300':
|
||||
@@ -46,8 +52,6 @@ export default {
|
||||
'border-red-200 hover:border-red-300 focus:border-red-300': error,
|
||||
}"
|
||||
:placeholder="placeholder"
|
||||
:value="value"
|
||||
@change="onChange"
|
||||
/>
|
||||
<div v-if="error" class="mt-2 text-xs font-medium text-red-400">
|
||||
{{ error }}
|
||||
|
||||
77
app/javascript/shared/components/charts/BarChart.vue
Normal file
77
app/javascript/shared/components/charts/BarChart.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { Bar } from 'vue-chartjs';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
Tooltip,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
} from 'chart.js';
|
||||
|
||||
const props = defineProps({
|
||||
collection: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
chartOptions: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
ChartJS.register(Title, Tooltip, BarElement, CategoryScale, LinearScale);
|
||||
|
||||
const fontFamily =
|
||||
'Inter,-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
|
||||
|
||||
const defaultChartOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
legend: {
|
||||
display: false,
|
||||
labels: {
|
||||
fontFamily,
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
datasets: {
|
||||
bar: {
|
||||
barPercentage: 1.0,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
ticks: {
|
||||
fontFamily: fontFamily,
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
position: 'left',
|
||||
ticks: {
|
||||
fontFamily: fontFamily,
|
||||
beginAtZero: true,
|
||||
stepSize: 1,
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const options = computed(() => {
|
||||
return { ...defaultChartOptions, ...props.chartOptions };
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Bar :data="collection" :options="options" />
|
||||
</template>
|
||||
@@ -184,6 +184,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.box-shadow-blue {
|
||||
box-shadow:
|
||||
@@ -200,7 +201,7 @@ export default {
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~dashboard/assets/scss/mixins';
|
||||
@import 'dashboard/assets/scss/mixins';
|
||||
|
||||
.emoji-dialog {
|
||||
&::before {
|
||||
@@ -210,6 +211,7 @@ export default {
|
||||
$color-bg-dark: #26292b;
|
||||
@include arrow(bottom, $color-bg-dark, $space-slab);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
$color-bg: #ebf0f5;
|
||||
@include arrow(bottom, $color-bg, $space-slab);
|
||||
@@ -245,6 +247,7 @@ export default {
|
||||
.emoji-icon {
|
||||
@apply text-slate-200 dark:text-slate-200 mb-2;
|
||||
}
|
||||
|
||||
.empty-message--text {
|
||||
@apply text-slate-200 dark:text-slate-200 text-sm font-medium;
|
||||
}
|
||||
@@ -271,6 +274,7 @@ export default {
|
||||
li .active {
|
||||
@apply bg-white dark:bg-slate-900;
|
||||
}
|
||||
|
||||
.emoji--item {
|
||||
@apply items-center flex text-sm;
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import Dropdown from './MultiselectDropdown';
|
||||
|
||||
export default {
|
||||
title: 'Components/Dropdown/Multiselect Dropdown',
|
||||
component: Dropdown,
|
||||
argTypes: {
|
||||
options: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
selectedItem: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
multiselectorTitle: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
multiselectorPlaceholder: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
noSearchResult: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
inputPlaceholder: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { Dropdown },
|
||||
template: '<dropdown v-bind="$props" @click="onClick"></dropdown>',
|
||||
});
|
||||
|
||||
export const MultiselectDropdown = Template.bind({});
|
||||
MultiselectDropdown.args = {
|
||||
onClick: action('Opened'),
|
||||
options: [
|
||||
{
|
||||
id: 1,
|
||||
availability_status: 'online',
|
||||
name: 'James Philip',
|
||||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg',
|
||||
},
|
||||
],
|
||||
|
||||
selectedItem: {
|
||||
id: 1,
|
||||
availability_status: 'online',
|
||||
name: 'James Philip',
|
||||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg',
|
||||
},
|
||||
};
|
||||
@@ -1,142 +1,130 @@
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, defineEmits } from 'vue';
|
||||
import { OnClickOutside } from '@vueuse/components';
|
||||
import { useToggle } from '@vueuse/core';
|
||||
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import MultiselectDropdownItems from 'shared/components/ui/MultiselectDropdownItems.vue';
|
||||
export default {
|
||||
components: {
|
||||
Thumbnail,
|
||||
MultiselectDropdownItems,
|
||||
},
|
||||
props: {
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
hasThumbnail: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
multiselectorTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multiselectorPlaceholder: {
|
||||
type: String,
|
||||
default: 'None',
|
||||
},
|
||||
noSearchResult: {
|
||||
type: String,
|
||||
default: 'No results found',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
default: 'Search',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSearchDropdown: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasValue() {
|
||||
if (this.selectedItem && this.selectedItem.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleDropdown() {
|
||||
this.showSearchDropdown = !this.showSearchDropdown;
|
||||
},
|
||||
|
||||
onCloseDropdown() {
|
||||
this.showSearchDropdown = false;
|
||||
},
|
||||
|
||||
onClickSelectItem(value) {
|
||||
this.$emit('click', value);
|
||||
this.onCloseDropdown();
|
||||
},
|
||||
const props = defineProps({
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
selectedItem: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
hasThumbnail: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
multiselectorTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
multiselectorPlaceholder: {
|
||||
type: String,
|
||||
default: 'None',
|
||||
},
|
||||
noSearchResult: {
|
||||
type: String,
|
||||
default: 'No results found',
|
||||
},
|
||||
inputPlaceholder: {
|
||||
type: String,
|
||||
default: 'Search',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['select']);
|
||||
const [showSearchDropdown, toggleDropdown] = useToggle(false);
|
||||
|
||||
const onCloseDropdown = () => toggleDropdown(false);
|
||||
const onClickSelectItem = value => {
|
||||
emit('select', value);
|
||||
onCloseDropdown();
|
||||
};
|
||||
|
||||
const hasValue = computed(() => {
|
||||
if (props.selectedItem && props.selectedItem.id) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-on-clickaway="onCloseDropdown"
|
||||
class="relative w-full mb-2"
|
||||
@keyup.esc="onCloseDropdown"
|
||||
>
|
||||
<woot-button
|
||||
variant="hollow"
|
||||
color-scheme="secondary"
|
||||
class="w-full border border-solid border-slate-100 dark:border-slate-700 px-2 hover:border-slate-75 dark:hover:border-slate-600"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<Thumbnail
|
||||
v-if="hasValue && hasThumbnail"
|
||||
:src="selectedItem.thumbnail"
|
||||
size="24px"
|
||||
:status="selectedItem.availability_status"
|
||||
:username="selectedItem.name"
|
||||
/>
|
||||
<div class="flex justify-between w-full min-w-0 items-center">
|
||||
<h4
|
||||
v-if="!hasValue"
|
||||
class="text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
>
|
||||
{{ multiselectorPlaceholder }}
|
||||
</h4>
|
||||
<h4
|
||||
v-else
|
||||
class="items-center leading-tight overflow-hidden whitespace-nowrap text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
:title="selectedItem.name"
|
||||
>
|
||||
{{ selectedItem.name }}
|
||||
</h4>
|
||||
<i
|
||||
v-if="showSearchDropdown"
|
||||
class="icon ion-chevron-up text-slate-600 mr-1"
|
||||
<OnClickOutside @trigger="onCloseDropdown">
|
||||
<div class="relative w-full mb-2" @keyup.esc="onCloseDropdown">
|
||||
<woot-button
|
||||
variant="hollow"
|
||||
color-scheme="secondary"
|
||||
class="w-full border border-solid border-slate-100 dark:border-slate-700 px-2 hover:border-slate-75 dark:hover:border-slate-600"
|
||||
@click="
|
||||
() => toggleDropdown() // ensure that the event is not passed to the button
|
||||
"
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<Thumbnail
|
||||
v-if="hasValue && hasThumbnail"
|
||||
:src="selectedItem.thumbnail"
|
||||
size="24px"
|
||||
:status="selectedItem.availability_status"
|
||||
:username="selectedItem.name"
|
||||
/>
|
||||
<i v-else class="icon ion-chevron-down text-slate-600 mr-1" />
|
||||
<div class="flex justify-between w-full min-w-0 items-center">
|
||||
<h4
|
||||
v-if="!hasValue"
|
||||
class="text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
>
|
||||
{{ multiselectorPlaceholder }}
|
||||
</h4>
|
||||
<h4
|
||||
v-else
|
||||
class="items-center leading-tight overflow-hidden whitespace-nowrap text-ellipsis text-sm text-slate-800 dark:text-slate-100"
|
||||
:title="selectedItem.name"
|
||||
>
|
||||
{{ selectedItem.name }}
|
||||
</h4>
|
||||
<i
|
||||
v-if="showSearchDropdown"
|
||||
class="icon i-lucide-chevron-up text-slate-600 mr-1"
|
||||
/>
|
||||
<i v-else class="icon i-lucide-chevron-down text-slate-600 mr-1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</woot-button>
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdown }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<h4
|
||||
class="text-sm text-slate-800 dark:text-slate-100 m-0 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ multiselectorTitle }}
|
||||
</h4>
|
||||
<woot-button
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
variant="clear"
|
||||
@click="onCloseDropdown"
|
||||
</woot-button>
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdown }"
|
||||
class="dropdown-pane"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-1">
|
||||
<h4
|
||||
class="text-sm text-slate-800 dark:text-slate-100 m-0 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ multiselectorTitle }}
|
||||
</h4>
|
||||
<woot-button
|
||||
icon="dismiss"
|
||||
size="tiny"
|
||||
color-scheme="secondary"
|
||||
variant="clear"
|
||||
@click="onCloseDropdown"
|
||||
/>
|
||||
</div>
|
||||
<MultiselectDropdownItems
|
||||
v-if="showSearchDropdown"
|
||||
:options="options"
|
||||
:selected-items="[selectedItem]"
|
||||
:has-thumbnail="hasThumbnail"
|
||||
:input-placeholder="inputPlaceholder"
|
||||
:no-search-result="noSearchResult"
|
||||
@select="onClickSelectItem"
|
||||
/>
|
||||
</div>
|
||||
<MultiselectDropdownItems
|
||||
v-if="showSearchDropdown"
|
||||
:options="options"
|
||||
:selected-items="[selectedItem]"
|
||||
:has-thumbnail="hasThumbnail"
|
||||
:input-placeholder="inputPlaceholder"
|
||||
:no-search-result="noSearchResult"
|
||||
@click="onClickSelectItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</OnClickOutside>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import DropdownItems from './MultiselectDropdownItems';
|
||||
|
||||
export default {
|
||||
title: 'Components/Dropdown/Multiselect Dropdown Items',
|
||||
component: DropdownItems,
|
||||
argTypes: {
|
||||
options: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
selectedItems: {
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
inputPlaceholder: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
noSearchResult: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { DropdownItems },
|
||||
template:
|
||||
'<dropdown-items v-bind="$props" @click="onClick"></dropdown-items>',
|
||||
});
|
||||
|
||||
export const MultiselectDropdownItems = Template.bind({});
|
||||
MultiselectDropdownItems.args = {
|
||||
onClick: action('Added'),
|
||||
options: [
|
||||
{
|
||||
id: '0',
|
||||
name: 'None',
|
||||
thumbnail: '',
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
name: 'James Philip',
|
||||
availability_status: 'online',
|
||||
role: 'administrator',
|
||||
thumbnail: 'https://randomuser.me/api/portraits/men/4.jpg',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Support Team',
|
||||
thumbnail: '',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Agent',
|
||||
thumbnail: '',
|
||||
},
|
||||
],
|
||||
|
||||
selectedItems: [{ id: '1' }],
|
||||
};
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
onclick(option) {
|
||||
this.$emit('click', option);
|
||||
this.$emit('select', option);
|
||||
},
|
||||
focusInput() {
|
||||
this.$refs.searchbar.focus();
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import AddLabel from './AddLabel';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Add Button',
|
||||
component: AddLabel,
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { AddLabel },
|
||||
template: '<add-label v-bind="$props" @add="onClick"></add-label>',
|
||||
});
|
||||
|
||||
export const AddButton = Template.bind({});
|
||||
AddButton.args = {
|
||||
onClick: action('opened'),
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import LabelDropdown from './LabelDropdown';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Dropdown',
|
||||
component: LabelDropdown,
|
||||
argTypes: {
|
||||
conversationId: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
accountLabels: {
|
||||
defaultValue: [
|
||||
{
|
||||
color: '#555',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'sales',
|
||||
},
|
||||
{
|
||||
color: '#c242f5',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'business',
|
||||
},
|
||||
{
|
||||
color: '#4287f5',
|
||||
description: '',
|
||||
id: 1,
|
||||
title: 'testing',
|
||||
},
|
||||
],
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
selectedLabels: {
|
||||
defaultValue: 'sales, testing',
|
||||
control: {
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { LabelDropdown },
|
||||
template:
|
||||
'<label-dropdown v-bind="$props" @add="onAdd" @remove="onRemove"></label-dropdown>',
|
||||
});
|
||||
|
||||
export const Dropdown = Template.bind({});
|
||||
Dropdown.args = {
|
||||
onAdd: action('added'),
|
||||
onRemove: action('removed'),
|
||||
};
|
||||
@@ -142,7 +142,7 @@ export default {
|
||||
:title="label.title"
|
||||
:color="label.color"
|
||||
:selected="selectedLabels.includes(label.title)"
|
||||
@click="onAddRemove(label)"
|
||||
@select-label="onAddRemove(label)"
|
||||
/>
|
||||
</woot-dropdown-menu>
|
||||
<div
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import LabelDropdownItem from './LabelDropdownItem';
|
||||
|
||||
export default {
|
||||
title: 'Components/Label/Item',
|
||||
component: LabelDropdownItem,
|
||||
argTypes: {
|
||||
title: {
|
||||
defaultValue: 'sales',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
color: {
|
||||
defaultValue: '#555',
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { LabelDropdownItem },
|
||||
template:
|
||||
'<label-dropdown-item v-bind="$props" @click="onClick"></label-dropdown-item>',
|
||||
});
|
||||
|
||||
export const Item = Template.bind({});
|
||||
Item.args = {
|
||||
onClick: action('Selected'),
|
||||
};
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
|
||||
methods: {
|
||||
onClick() {
|
||||
this.$emit('click', this.title);
|
||||
this.$emit('selectLabel', this.title);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
<span class="label-text" :title="title">{{ title }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<i v-if="selected" class="icon ion-checkmark-round" />
|
||||
<i v-if="selected" class="i-lucide-circle-check" />
|
||||
</div>
|
||||
</div>
|
||||
</woot-button>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { useCampaign } from '../useCampaign';
|
||||
import { useRoute } from 'dashboard/composables/route';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { CAMPAIGN_TYPES } from '../../constants/campaign';
|
||||
|
||||
// Mock the useRoute composable
|
||||
vi.mock('dashboard/composables/route', () => ({
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: vi.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { useFilter } from '../useFilter';
|
||||
import { useStore } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
// Mock the dependencies
|
||||
vi.mock('dashboard/composables/store');
|
||||
vi.mock('dashboard/composables/useI18n');
|
||||
vi.mock('vue-i18n');
|
||||
|
||||
describe('useFilter', () => {
|
||||
// Setup mocks
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'dashboard/composables/route';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { CAMPAIGN_TYPES } from '../constants/campaign';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { useStore } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { filterAttributeGroups as conversationFilterAttributeGroups } from 'dashboard/components/widgets/conversation/advancedFilterItems';
|
||||
import { filterAttributeGroups as contactFilterAttributeGroups } from 'dashboard/routes/dashboard/contacts/contactFilterItems';
|
||||
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import mila from 'markdown-it-link-attributes';
|
||||
import mentionPlugin from './markdownIt/link';
|
||||
|
||||
import MarkdownIt from 'markdown-it';
|
||||
const setImageHeight = inlineToken => {
|
||||
const imgSrc = inlineToken.attrGet('src');
|
||||
if (!imgSrc) return;
|
||||
@@ -30,7 +30,7 @@ const imgResizeManager = md => {
|
||||
});
|
||||
};
|
||||
|
||||
const md = require('markdown-it')({
|
||||
const md = MarkdownIt({
|
||||
html: false,
|
||||
xhtmlOut: true,
|
||||
breaks: true,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
export const set = (state, data) => {
|
||||
state.records = data;
|
||||
};
|
||||
@@ -20,7 +18,7 @@ export const setSingleRecord = (state, data) => {
|
||||
export const update = (state, data) => {
|
||||
state.records.forEach((element, index) => {
|
||||
if (element.id === data.id) {
|
||||
Vue.set(state.records, index, data);
|
||||
state.records[index] = data;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -29,7 +27,7 @@ export const update = (state, data) => {
|
||||
export const updateAttributes = (state, data) => {
|
||||
state.records.forEach((element, index) => {
|
||||
if (element.id === data.id) {
|
||||
Vue.set(state.records, index, { ...state.records[index], ...data });
|
||||
state.records[index] = { ...state.records[index], ...data };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -37,11 +35,7 @@ export const updateAttributes = (state, data) => {
|
||||
export const updatePresence = (state, data) => {
|
||||
state.records.forEach((element, index) => {
|
||||
const availabilityStatus = data[element.id];
|
||||
Vue.set(
|
||||
state.records[index],
|
||||
'availability_status',
|
||||
availabilityStatus || 'offline'
|
||||
);
|
||||
state.records[index].availability_status = availabilityStatus || 'offline';
|
||||
});
|
||||
};
|
||||
|
||||
@@ -51,7 +45,7 @@ export const updateSingleRecordPresence = (
|
||||
) => {
|
||||
const [selectedRecord] = records.filter(record => record.id === Number(id));
|
||||
if (selectedRecord) {
|
||||
Vue.set(selectedRecord, 'availability_status', availabilityStatus);
|
||||
selectedRecord.availability_status = availabilityStatus;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
this.addEventHandler(keydownHandler);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
unmounted() {
|
||||
if (this.$el && this.$el.dataset?.keydownHandlerIndex) {
|
||||
const handlerToRemove =
|
||||
taggedHandlers[this.$el.dataset.keydownHandlerIndex];
|
||||
|
||||
Reference in New Issue
Block a user