feat: Eslint rules (#9839)
# Pull Request Template ## Description This PR adds new eslint rules to the code base. **Error rules** | Rule name | Type | Files updated | | ----------------- | --- | - | | `vue/block-order` | error | ✅ | | `vue/component-name-in-template-casing` | error | ✅ | | `vue/component-options-name-casing` | error | ✅ | | `vue/custom-event-name-casing` | error | ✅ | | `vue/define-emits-declaration` | error | ✅ | | `vue/no-unused-properties` | error | ✅ | | `vue/define-macros-order` | error | ✅ | | `vue/define-props-declaration` | error | ✅ | | `vue/match-component-import-name` | error | ✅ | | `vue/next-tick-style` | error | ✅ | | `vue/no-bare-strings-in-template` | error | ✅ | | `vue/no-empty-component-block` | error | ✅ | | `vue/no-multiple-objects-in-class` | error | ✅ | | `vue/no-required-prop-with-default` | error | ✅ | | `vue/no-static-inline-styles` | error | ✅ | | `vue/no-template-target-blank` | error | ✅ | | `vue/no-this-in-before-route-enter` | error | ✅ | | `vue/no-undef-components` | error | ✅ | | `vue/no-unused-emit-declarations` | error | ✅ | | `vue/no-unused-refs` | error | ✅ | | `vue/no-use-v-else-with-v-for` | error | ✅ | | `vue/no-useless-v-bind` | error | ✅ | | `vue/no-v-text` | error | ✅ | | `vue/padding-line-between-blocks` | error | ✅ | | ~`vue/prefer-prop-type-boolean-first`~ | ~error~ | ❌ (removed this rule, cause a bug in displaying custom attributes) | | `vue/prefer-separate-static-class` | error | ✅ | | `vue/prefer-true-attribute-shorthand` | error | ✅ | | `vue/require-explicit-slots` | error | ✅ | | `vue/require-macro-variable-name` | error | ✅ | **Warn rules** | Rule name | Type | Files updated | | ---- | ------------- | ------------- | | `vue/no-root-v-if` | warn | ❎ | Fixes https://linear.app/chatwoot/issue/CW-3492/vue-eslint-rules ## Type of change - [x] New feature (non-breaking change which adds functionality) ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Fayaz Ahmed <fayazara@gmail.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -1,20 +1,3 @@
|
||||
<template>
|
||||
<blockquote
|
||||
ref="messageContainer"
|
||||
class="message border-l-2 border-slate-100 dark:border-slate-700"
|
||||
>
|
||||
<p class="header">
|
||||
<strong class="text-slate-700 dark:text-slate-100">
|
||||
{{ author }}
|
||||
</strong>
|
||||
{{ $t('SEARCH.WROTE') }}
|
||||
</p>
|
||||
<read-more :shrink="isOverflowing" @expand="isOverflowing = false">
|
||||
<div v-dompurify-html="prepareContent(content)" class="message-content" />
|
||||
</read-more>
|
||||
</blockquote>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import messageFormatterMixin from 'shared/mixins/messageFormatterMixin';
|
||||
import ReadMore from './ReadMore.vue';
|
||||
@@ -81,6 +64,23 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<blockquote
|
||||
ref="messageContainer"
|
||||
class="message border-l-2 border-slate-100 dark:border-slate-700"
|
||||
>
|
||||
<p class="header">
|
||||
<strong class="text-slate-700 dark:text-slate-100">
|
||||
{{ author }}
|
||||
</strong>
|
||||
{{ $t('SEARCH.WROTE') }}
|
||||
</p>
|
||||
<ReadMore :shrink="isOverflowing" @expand="isOverflowing = false">
|
||||
<div v-dompurify-html="prepareContent(content)" class="message-content" />
|
||||
</ReadMore>
|
||||
</blockquote>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.message {
|
||||
@apply py-0 px-2 mt-2;
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
shrink: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="read-more">
|
||||
<div
|
||||
ref="content"
|
||||
:class="{
|
||||
'shrink-container after:shrink-gradient-light dark:after:shrink-gradient-dark':
|
||||
shrink,
|
||||
@@ -22,17 +32,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
shrink: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@tailwind components;
|
||||
@layer components {
|
||||
@@ -56,7 +55,6 @@ export default {
|
||||
@apply max-h-[100px] overflow-hidden relative;
|
||||
}
|
||||
.shrink-container::after {
|
||||
@apply content-[''] absolute bottom-0 left-0 right-0 h-[50px] z-10;
|
||||
}
|
||||
.read-more-button {
|
||||
@apply max-w-max absolute bottom-2 left-0 right-0 mx-auto mt-0 z-20 shadow-sm;
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
<template>
|
||||
<div class="input-container" :class="{ 'is-focused': isInputFocused }">
|
||||
<div class="icon-container">
|
||||
<fluent-icon icon="search" class="icon" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
ref="searchInput"
|
||||
type="search"
|
||||
class="dark:bg-slate-900"
|
||||
:placeholder="$t('SEARCH.INPUT_PLACEHOLDER')"
|
||||
:value="searchQuery"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@input="debounceSearch"
|
||||
/>
|
||||
<woot-label
|
||||
:title="$t('SEARCH.PLACEHOLDER_KEYBINDING')"
|
||||
:show-close="false"
|
||||
small
|
||||
class="helper-label"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
@@ -71,6 +47,30 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input-container" :class="{ 'is-focused': isInputFocused }">
|
||||
<div class="icon-container">
|
||||
<fluent-icon icon="search" class="icon" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
ref="searchInput"
|
||||
type="search"
|
||||
class="dark:bg-slate-900"
|
||||
:placeholder="$t('SEARCH.INPUT_PLACEHOLDER')"
|
||||
:value="searchQuery"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
@input="debounceSearch"
|
||||
/>
|
||||
<woot-label
|
||||
:title="$t('SEARCH.PLACEHOLDER_KEYBINDING')"
|
||||
:show-close="false"
|
||||
small
|
||||
class="helper-label"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-container {
|
||||
transition: border-bottom 0.2s ease-in-out;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="search-input">
|
||||
<fluent-icon icon="search" size="14px" class="search--icon" />
|
||||
<span
|
||||
class="text-ellipsis overflow-hidden whitespace-nowrap search-placeholder"
|
||||
class="overflow-hidden text-ellipsis whitespace-nowrap search-placeholder"
|
||||
>
|
||||
{{ $t('CONVERSATION.SEARCH_MESSAGES') }}
|
||||
</span>
|
||||
@@ -18,20 +18,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
tabs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.search-input-box {
|
||||
@apply p-2;
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
<template>
|
||||
<router-link :to="navigateTo" class="contact-item">
|
||||
<woot-thumbnail :src="thumbnail" :username="name" size="24px" />
|
||||
<div class="ml-2 rtl:mr-2 rtl:ml-0">
|
||||
<h5 class="text-sm name text-slate-800 dark:text-slate-200">
|
||||
{{ name }}
|
||||
</h5>
|
||||
<p
|
||||
class="m-0 text-slate-600 dark:text-slate-200 gap-1 text-sm flex items-center"
|
||||
>
|
||||
<span v-if="email" class="email text-slate-800 dark:text-slate-200">{{
|
||||
email
|
||||
}}</span>
|
||||
<span v-if="phone" class="separator text-slate-700 dark:text-slate-200">
|
||||
•
|
||||
</span>
|
||||
<span v-if="phone" class="phone text-slate-800 dark:text-slate-200">
|
||||
{{ phone }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||
export default {
|
||||
@@ -59,6 +35,30 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link :to="navigateTo" class="contact-item">
|
||||
<woot-thumbnail :src="thumbnail" :username="name" size="24px" />
|
||||
<div class="ml-2 rtl:mr-2 rtl:ml-0">
|
||||
<h5 class="text-sm name text-slate-800 dark:text-slate-200">
|
||||
{{ name }}
|
||||
</h5>
|
||||
<p
|
||||
class="m-0 text-slate-600 dark:text-slate-200 gap-1 text-sm flex items-center"
|
||||
>
|
||||
<span v-if="email" class="email text-slate-800 dark:text-slate-200">{{
|
||||
email
|
||||
}}</span>
|
||||
<span v-if="phone" class="separator text-slate-700 dark:text-slate-200">
|
||||
•
|
||||
</span>
|
||||
<span v-if="phone" class="phone text-slate-800 dark:text-slate-200">
|
||||
{{ phone }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contact-item {
|
||||
@apply cursor-pointer flex items-center p-2 rounded-sm hover:bg-slate-25 dark:hover:bg-slate-800;
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
<template>
|
||||
<search-result-section
|
||||
:title="$t('SEARCH.SECTION.CONTACTS')"
|
||||
:empty="!contacts.length"
|
||||
:query="query"
|
||||
:show-title="showTitle"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="contacts.length" class="search-list">
|
||||
<li v-for="contact in contacts" :key="contact.id">
|
||||
<search-result-contact-item
|
||||
:id="contact.id"
|
||||
:name="contact.name"
|
||||
:email="contact.email"
|
||||
:phone="contact.phone_number"
|
||||
:account-id="accountId"
|
||||
:thumbnail="contact.thumbnail"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</search-result-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
@@ -57,3 +34,26 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SearchResultSection
|
||||
:title="$t('SEARCH.SECTION.CONTACTS')"
|
||||
:empty="!contacts.length"
|
||||
:query="query"
|
||||
:show-title="showTitle"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="contacts.length" class="search-list">
|
||||
<li v-for="contact in contacts" :key="contact.id">
|
||||
<SearchResultContactItem
|
||||
:id="contact.id"
|
||||
:name="contact.name"
|
||||
:email="contact.email"
|
||||
:phone="contact.phone_number"
|
||||
:account-id="accountId"
|
||||
:thumbnail="contact.thumbnail"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</SearchResultSection>
|
||||
</template>
|
||||
|
||||
@@ -1,43 +1,3 @@
|
||||
<template>
|
||||
<router-link :to="navigateTo" class="conversation-item">
|
||||
<div class="icon-wrap">
|
||||
<fluent-icon icon="chat-multiple" :size="14" />
|
||||
</div>
|
||||
<div class="conversation-details">
|
||||
<div class="meta-wrap">
|
||||
<div class="flex">
|
||||
<woot-label
|
||||
class="conversation-id"
|
||||
:title="`#${id}`"
|
||||
:show-close="false"
|
||||
small
|
||||
/>
|
||||
<div class="inbox-name-wrap">
|
||||
<inbox-name :inbox="inbox" class="mr-2 rtl:mr-0 rtl:ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="created-at">{{ createdAtTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<h5 v-if="name" class="text-sm name text-slate-800 dark:text-slate-100">
|
||||
<span class="pre-text"> {{ $t('SEARCH.FROM') }}: </span>
|
||||
{{ name }}
|
||||
</h5>
|
||||
<h5
|
||||
v-if="email"
|
||||
class="overflow-hidden text-sm email text-slate-700 dark:text-slate-200 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
<span class="pre-text">{{ $t('SEARCH.EMAIL') }}:</span>
|
||||
{{ email }}
|
||||
</h5>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { frontendURL } from 'dashboard/helper/URLHelper.js';
|
||||
import { dynamicTime } from 'shared/helpers/timeHelper';
|
||||
@@ -95,6 +55,46 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link :to="navigateTo" class="conversation-item">
|
||||
<div class="icon-wrap">
|
||||
<fluent-icon icon="chat-multiple" :size="14" />
|
||||
</div>
|
||||
<div class="conversation-details">
|
||||
<div class="meta-wrap">
|
||||
<div class="flex">
|
||||
<woot-label
|
||||
class="conversation-id"
|
||||
:title="`#${id}`"
|
||||
:show-close="false"
|
||||
small
|
||||
/>
|
||||
<div class="inbox-name-wrap">
|
||||
<InboxName :inbox="inbox" class="mr-2 rtl:mr-0 rtl:ml-2" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="created-at">{{ createdAtTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<h5 v-if="name" class="text-sm name text-slate-800 dark:text-slate-100">
|
||||
<span class="pre-text"> {{ $t('SEARCH.FROM') }}: </span>
|
||||
{{ name }}
|
||||
</h5>
|
||||
<h5
|
||||
v-if="email"
|
||||
class="overflow-hidden text-sm email text-slate-700 dark:text-slate-200 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
<span class="pre-text">{{ $t('SEARCH.EMAIL') }}:</span>
|
||||
{{ email }}
|
||||
</h5>
|
||||
</div>
|
||||
<slot />
|
||||
</div>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.conversation-item {
|
||||
@apply cursor-pointer flex p-2 rounded hover:bg-slate-25 dark:hover:bg-slate-800;
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
<template>
|
||||
<search-result-section
|
||||
:title="$t('SEARCH.SECTION.CONVERSATIONS')"
|
||||
:empty="!conversations.length"
|
||||
:query="query"
|
||||
:show-title="showTitle || isFetching"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="conversations.length" class="search-list">
|
||||
<li v-for="conversation in conversations" :key="conversation.id">
|
||||
<search-result-conversation-item
|
||||
:id="conversation.id"
|
||||
:name="conversation.contact.name"
|
||||
:email="conversation.contact.email"
|
||||
:account-id="accountId"
|
||||
:inbox="conversation.inbox"
|
||||
:created-at="conversation.created_at"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</search-result-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import SearchResultSection from './SearchResultSection.vue';
|
||||
@@ -56,3 +33,26 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SearchResultSection
|
||||
:title="$t('SEARCH.SECTION.CONVERSATIONS')"
|
||||
:empty="!conversations.length"
|
||||
:query="query"
|
||||
:show-title="showTitle || isFetching"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="conversations.length" class="search-list">
|
||||
<li v-for="conversation in conversations" :key="conversation.id">
|
||||
<SearchResultConversationItem
|
||||
:id="conversation.id"
|
||||
:name="conversation.contact.name"
|
||||
:email="conversation.contact.email"
|
||||
:account-id="accountId"
|
||||
:inbox="conversation.inbox"
|
||||
:created-at="conversation.created_at"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</SearchResultSection>
|
||||
</template>
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
<template>
|
||||
<search-result-section
|
||||
:title="$t('SEARCH.SECTION.MESSAGES')"
|
||||
:empty="!messages.length"
|
||||
:query="query"
|
||||
:show-title="showTitle"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="messages.length" class="search-list">
|
||||
<li v-for="message in messages" :key="message.id">
|
||||
<search-result-conversation-item
|
||||
:id="message.conversation_id"
|
||||
:account-id="accountId"
|
||||
:inbox="message.inbox"
|
||||
:created-at="message.created_at"
|
||||
:message-id="message.id"
|
||||
>
|
||||
<message-content
|
||||
:author="getName(message)"
|
||||
:content="message.content"
|
||||
:search-term="query"
|
||||
/>
|
||||
</search-result-conversation-item>
|
||||
</li>
|
||||
</ul>
|
||||
</search-result-section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import SearchResultConversationItem from './SearchResultConversationItem.vue';
|
||||
@@ -70,3 +42,31 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SearchResultSection
|
||||
:title="$t('SEARCH.SECTION.MESSAGES')"
|
||||
:empty="!messages.length"
|
||||
:query="query"
|
||||
:show-title="showTitle"
|
||||
:is-fetching="isFetching"
|
||||
>
|
||||
<ul v-if="messages.length" class="search-list">
|
||||
<li v-for="message in messages" :key="message.id">
|
||||
<SearchResultConversationItem
|
||||
:id="message.conversation_id"
|
||||
:account-id="accountId"
|
||||
:inbox="message.inbox"
|
||||
:created-at="message.created_at"
|
||||
:message-id="message.id"
|
||||
>
|
||||
<MessageContent
|
||||
:author="getName(message)"
|
||||
:content="message.content"
|
||||
:search-term="query"
|
||||
/>
|
||||
</SearchResultConversationItem>
|
||||
</li>
|
||||
</ul>
|
||||
</SearchResultSection>
|
||||
</template>
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
<template>
|
||||
<section class="result-section">
|
||||
<div v-if="showTitle" class="header">
|
||||
<h3 class="text-sm text-slate-800 dark:text-slate-100">{{ title }}</h3>
|
||||
</div>
|
||||
<woot-loading-state v-if="isFetching" :message="'Searching'" />
|
||||
<slot v-else />
|
||||
<div v-if="empty && !isFetching" class="empty">
|
||||
<fluent-icon icon="info" size="16px" class="icon" />
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE', { item: titleCase, query }) }}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
@@ -46,6 +30,25 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="result-section">
|
||||
<div v-if="showTitle" class="header">
|
||||
<h3 class="text-sm text-slate-800 dark:text-slate-100">{{ title }}</h3>
|
||||
</div>
|
||||
<woot-loading-state
|
||||
v-if="isFetching"
|
||||
:message="$t('SEARCH.SEARCHING_DATA')"
|
||||
/>
|
||||
<slot v-else />
|
||||
<div v-if="empty && !isFetching" class="empty">
|
||||
<fluent-icon icon="info" size="16px" class="icon" />
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE', { item: titleCase, query }) }}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.result-section {
|
||||
@apply my-2 mx-0;
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
<template>
|
||||
<div class="tab-container">
|
||||
<woot-tabs :index="activeTab" :border="false" @change="onTabChange">
|
||||
<woot-tabs-item
|
||||
v-for="item in tabs"
|
||||
:key="item.key"
|
||||
:name="item.name"
|
||||
:count="item.count"
|
||||
/>
|
||||
</woot-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
@@ -38,11 +25,25 @@ export default {
|
||||
methods: {
|
||||
onTabChange(index) {
|
||||
this.activeTab = index;
|
||||
this.$emit('tab-change', this.tabs[index].key);
|
||||
this.$emit('tabChange', this.tabs[index].key);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tab-container">
|
||||
<woot-tabs :index="activeTab" :border="false" @change="onTabChange">
|
||||
<woot-tabs-item
|
||||
v-for="item in tabs"
|
||||
:key="item.key"
|
||||
:name="item.name"
|
||||
:count="item.count"
|
||||
/>
|
||||
</woot-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tab-container {
|
||||
@apply mt-1 border-b border-solid border-slate-100 dark:border-slate-800/50;
|
||||
|
||||
@@ -1,71 +1,3 @@
|
||||
<template>
|
||||
<div class="search-page">
|
||||
<div class="page-header">
|
||||
<woot-button
|
||||
icon="chevron-left"
|
||||
variant="smooth"
|
||||
size="small "
|
||||
class="back-button"
|
||||
@click="onBack"
|
||||
>
|
||||
{{ $t('GENERAL_SETTINGS.BACK') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
<section class="search-root">
|
||||
<header>
|
||||
<search-header @search="onSearch" />
|
||||
<search-tabs
|
||||
v-if="query"
|
||||
:tabs="tabs"
|
||||
:selected-tab="activeTabIndex"
|
||||
@tab-change="tab => (selectedTab = tab)"
|
||||
/>
|
||||
</header>
|
||||
<div class="search-results">
|
||||
<div v-if="showResultsSection">
|
||||
<search-result-contacts-list
|
||||
v-if="filterContacts"
|
||||
:is-fetching="uiFlags.contact.isFetching"
|
||||
:contacts="contacts"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
|
||||
<search-result-messages-list
|
||||
v-if="filterMessages"
|
||||
:is-fetching="uiFlags.message.isFetching"
|
||||
:messages="messages"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
|
||||
<search-result-conversations-list
|
||||
v-if="filterConversations"
|
||||
:is-fetching="uiFlags.conversation.isFetching"
|
||||
:conversations="conversations"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="showEmptySearchResults" class="empty">
|
||||
<fluent-icon icon="info" size="16px" class="icon" />
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE_FULL', { query }) }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="empty text-center">
|
||||
<p class="text-center margin-bottom-0">
|
||||
<fluent-icon icon="search" size="24px" class="icon" />
|
||||
</p>
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE_DEFAULT') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SearchHeader from './SearchHeader.vue';
|
||||
import SearchTabs from './SearchTabs.vue';
|
||||
@@ -208,6 +140,74 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search-page">
|
||||
<div class="page-header">
|
||||
<woot-button
|
||||
icon="chevron-left"
|
||||
variant="smooth"
|
||||
size="small "
|
||||
class="back-button"
|
||||
@click="onBack"
|
||||
>
|
||||
{{ $t('GENERAL_SETTINGS.BACK') }}
|
||||
</woot-button>
|
||||
</div>
|
||||
<section class="search-root">
|
||||
<header>
|
||||
<SearchHeader @search="onSearch" />
|
||||
<SearchTabs
|
||||
v-if="query"
|
||||
:tabs="tabs"
|
||||
:selected-tab="activeTabIndex"
|
||||
@tabChange="tab => (selectedTab = tab)"
|
||||
/>
|
||||
</header>
|
||||
<div class="search-results">
|
||||
<div v-if="showResultsSection">
|
||||
<SearchResultContactsList
|
||||
v-if="filterContacts"
|
||||
:is-fetching="uiFlags.contact.isFetching"
|
||||
:contacts="contacts"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
|
||||
<SearchResultMessagesList
|
||||
v-if="filterMessages"
|
||||
:is-fetching="uiFlags.message.isFetching"
|
||||
:messages="messages"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
|
||||
<SearchResultConversationsList
|
||||
v-if="filterConversations"
|
||||
:is-fetching="uiFlags.conversation.isFetching"
|
||||
:conversations="conversations"
|
||||
:query="query"
|
||||
:show-title="isSelectedTabAll"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="showEmptySearchResults" class="empty">
|
||||
<fluent-icon icon="info" size="16px" class="icon" />
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE_FULL', { query }) }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else class="text-center empty">
|
||||
<p class="text-center margin-bottom-0">
|
||||
<fluent-icon icon="search" size="24px" class="icon" />
|
||||
</p>
|
||||
<p class="empty-state__text">
|
||||
{{ $t('SEARCH.EMPTY_STATE_DEFAULT') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-page {
|
||||
@apply flex flex-col w-full bg-white dark:bg-slate-900;
|
||||
|
||||
Reference in New Issue
Block a user