Merge branch 'develop' into fix/cw-4085
This commit is contained in:
@@ -28,8 +28,8 @@ export default {
|
||||
'border-b border-solid border-n-weak/60 dark:border-n-weak': showBorder,
|
||||
}"
|
||||
>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-6 gap-6">
|
||||
<div class="col-span-2 xl:col-span-1">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-8 gap-6">
|
||||
<div class="col-span-2">
|
||||
<p
|
||||
v-if="title"
|
||||
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
|
||||
@@ -48,7 +48,7 @@ export default {
|
||||
{{ note }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-span-4 lg:col-span-4 2xl:col-span-3">
|
||||
<div class="col-span-6 xl:col-span-5">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import Button from 'dashboard/components-next/button/Button.vue';
|
||||
import FilterSelect from 'dashboard/components-next/filter/inputs/FilterSelect.vue';
|
||||
|
||||
const props = defineProps({
|
||||
table: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
defaultPageSize: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
showPageSizeSelector: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['pageSizeChange']);
|
||||
const { t } = useI18n();
|
||||
|
||||
const pageSizeOptions = [
|
||||
{
|
||||
label: `${t('REPORT.PAGINATION.PER_PAGE_TEMPLATE', { size: 10 })}`,
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
label: `${t('REPORT.PAGINATION.PER_PAGE_TEMPLATE', { size: 20 })}`,
|
||||
value: 20,
|
||||
},
|
||||
{
|
||||
label: `${t('REPORT.PAGINATION.PER_PAGE_TEMPLATE', { size: 50 })}`,
|
||||
value: 50,
|
||||
},
|
||||
{
|
||||
label: `${t('REPORT.PAGINATION.PER_PAGE_TEMPLATE', { size: 100 })}`,
|
||||
value: 100,
|
||||
},
|
||||
];
|
||||
|
||||
const getFormattedPages = (start, end) => {
|
||||
const formatter = new Intl.NumberFormat(navigator.language);
|
||||
return Array.from({ length: end - start + 1 }, (_, i) =>
|
||||
@@ -48,70 +83,98 @@ const end = computed(() => {
|
||||
total.value
|
||||
);
|
||||
});
|
||||
|
||||
const currentPageSize = computed({
|
||||
get() {
|
||||
return props.table.getState().pagination.pageSize;
|
||||
},
|
||||
set(newValue) {
|
||||
props.table.setPageSize(Number(newValue));
|
||||
emit('pageSizeChange', Number(newValue));
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
props.showPageSizeSelector &&
|
||||
props.defaultPageSize &&
|
||||
props.defaultPageSize !== 10
|
||||
) {
|
||||
props.table.setPageSize(props.defaultPageSize);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-1 items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-n-slate-11 mb-0">
|
||||
{{ $t('REPORT.PAGINATION.RESULTS', { start, end, total }) }}
|
||||
</p>
|
||||
</div>
|
||||
<nav class="isolate inline-flex gap-1">
|
||||
<woot-button
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
variant="clear"
|
||||
class="h-8 border-0 flex items-center"
|
||||
color-scheme="secondary"
|
||||
@click="table.setPageIndex(0)"
|
||||
>
|
||||
<span class="i-lucide-chevrons-left size-3" aria-hidden="true" />
|
||||
</woot-button>
|
||||
<woot-button
|
||||
variant="clear"
|
||||
class="h-8 border-0 flex items-center"
|
||||
color-scheme="secondary"
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
@click="table.previousPage()"
|
||||
>
|
||||
<span class="i-lucide-chevron-left size-3" aria-hidden="true" />
|
||||
</woot-button>
|
||||
<woot-button
|
||||
v-for="page in visiblePages"
|
||||
:key="page"
|
||||
variant="clear"
|
||||
class="h-8 flex items-center justify-center text-xs leading-none text-center"
|
||||
:class="page == currentPage ? 'border-n-brand' : 'border-slate-50'"
|
||||
color-scheme="secondary"
|
||||
@click="table.setPageIndex(page - 1)"
|
||||
>
|
||||
<span
|
||||
class="text-center"
|
||||
:class="{ 'text-n-brand': page == currentPage }"
|
||||
<div class="flex flex-1 items-center gap-2 justify-between">
|
||||
<p class="text-sm truncate text-n-slate-11 mb-0">
|
||||
{{ $t('REPORT.PAGINATION.RESULTS', { start, end, total }) }}
|
||||
</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<FilterSelect
|
||||
v-if="showPageSizeSelector"
|
||||
v-model="currentPageSize"
|
||||
variant="outline"
|
||||
hide-icon
|
||||
class="[&>button]:text-n-slate-11 [&>button]:hover:text-n-slate-12 [&>button]:h-6"
|
||||
:options="pageSizeOptions"
|
||||
/>
|
||||
<nav class="isolate inline-flex items-center gap-1.5">
|
||||
<Button
|
||||
icon="i-lucide-chevrons-left"
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="!size-6"
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
@click="table.setPageIndex(0)"
|
||||
/>
|
||||
<Button
|
||||
icon="i-lucide-chevron-left"
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="!size-6"
|
||||
:disabled="!table.getCanPreviousPage()"
|
||||
@click="table.previousPage()"
|
||||
/>
|
||||
<Button
|
||||
v-for="page in visiblePages"
|
||||
:key="page"
|
||||
xs
|
||||
outline
|
||||
:color="page == currentPage ? 'blue' : 'slate'"
|
||||
class="!h-6 min-w-6"
|
||||
@click="table.setPageIndex(page - 1)"
|
||||
>
|
||||
{{ page }}
|
||||
</span>
|
||||
</woot-button>
|
||||
<woot-button
|
||||
:disabled="!table.getCanNextPage()"
|
||||
variant="clear"
|
||||
class="h-8 border-0 flex items-center"
|
||||
color-scheme="secondary"
|
||||
@click="table.nextPage()"
|
||||
>
|
||||
<span class="i-lucide-chevron-right size-3" aria-hidden="true" />
|
||||
</woot-button>
|
||||
<woot-button
|
||||
:disabled="!table.getCanNextPage()"
|
||||
variant="clear"
|
||||
class="h-8 border-0 flex items-center"
|
||||
color-scheme="secondary"
|
||||
@click="table.setPageIndex(table.getPageCount() - 1)"
|
||||
>
|
||||
<span class="i-lucide-chevrons-right size-3" aria-hidden="true" />
|
||||
</woot-button>
|
||||
</nav>
|
||||
<span
|
||||
class="text-center"
|
||||
:class="{ 'text-n-brand': page == currentPage }"
|
||||
>
|
||||
{{ page }}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
icon="i-lucide-chevron-right"
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="!size-6"
|
||||
:disabled="!table.getCanNextPage()"
|
||||
@click="table.nextPage()"
|
||||
/>
|
||||
<Button
|
||||
icon="i-lucide-chevrons-right"
|
||||
ghost
|
||||
slate
|
||||
sm
|
||||
class="!size-6"
|
||||
:disabled="!table.getCanNextPage()"
|
||||
@click="table.setPageIndex(table.getPageCount() - 1)"
|
||||
/>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -68,7 +68,7 @@ export default {
|
||||
<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;
|
||||
@apply cursor-pointer after:bg-slate-75 before:bg-slate-75 dark:after:bg-slate-600 dark:before:bg-slate-600 py-4 ltr:pr-4 rtl:pl-4 ltr:pl-6 rtl:pr-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 rtl:after:left-6 rtl:before:left-6;
|
||||
|
||||
&.active {
|
||||
h3 {
|
||||
|
||||
@@ -177,6 +177,7 @@ export default {
|
||||
icon="dismiss"
|
||||
variant="clear"
|
||||
color-scheme="secondary"
|
||||
class="flex-shrink-0"
|
||||
@click="removeAction"
|
||||
/>
|
||||
</div>
|
||||
@@ -201,10 +202,10 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filter {
|
||||
@apply bg-n-slate-3 dark:bg-n-solid-3 p-2 border border-solid border-n-strong dark:border-n-strong rounded-md mb-2;
|
||||
@apply bg-n-background p-2 border border-solid border-n-strong dark:border-n-strong rounded-lg mb-2;
|
||||
|
||||
&.is-a-macro {
|
||||
@apply mb-0 bg-n-slate-2 dark:bg-n-solid-3 p-0 border-0 rounded-none;
|
||||
@apply mb-0 bg-n-background dark:bg-n-solid-1 p-0 border-0 rounded-none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,15 +214,19 @@ export default {
|
||||
}
|
||||
|
||||
.filter.has-error {
|
||||
@apply bg-red-50 dark:bg-red-800/50 border-red-100 dark:border-red-700/50;
|
||||
@apply bg-n-ruby-8/20 border-n-ruby-5 dark:border-n-ruby-5;
|
||||
|
||||
&.is-a-macro {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-inputs {
|
||||
@apply flex;
|
||||
@apply flex gap-1;
|
||||
}
|
||||
|
||||
.filter-error {
|
||||
@apply text-red-500 dark:text-red-200 block my-1 mx-0;
|
||||
@apply text-n-ruby-9 dark:text-n-ruby-9 block my-1 mx-0;
|
||||
}
|
||||
|
||||
.action__question,
|
||||
|
||||
@@ -29,7 +29,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="multiselect-wrap--small">
|
||||
<div class="multiselect-wrap--small flex flex-col gap-1 mt-1">
|
||||
<multiselect
|
||||
v-model="selectedTeams"
|
||||
track-by="id"
|
||||
|
||||
@@ -11,13 +11,13 @@ export default {
|
||||
<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-xl font-thin"
|
||||
class="text-n-slate-12 block text-center w-full text-xl font-medium"
|
||||
>
|
||||
{{ title }}
|
||||
</h3>
|
||||
<p
|
||||
v-if="message"
|
||||
class="block text-center text-slate-500 dark:text-slate-400 my-4 mx-auto w-[90%]"
|
||||
class="block text-center text-n-slate-11 dark:text-slate-400 my-4 mx-auto w-[90%]"
|
||||
>
|
||||
{{ message }}
|
||||
</p>
|
||||
|
||||
@@ -127,8 +127,8 @@ export default {
|
||||
},
|
||||
getInputErrorClass(errorMessage) {
|
||||
return errorMessage
|
||||
? 'bg-red-50 dark:bg-red-800/50 border-red-100 dark:border-red-700/50'
|
||||
: 'bg-n-slate-3 dark:bg-n-solid-3 border-n-strong dark:border-n-strong';
|
||||
? 'bg-n-ruby-8/20 border-n-ruby-5 dark:border-n-ruby-5'
|
||||
: 'bg-n-background border-n-weak dark:border-n-weak';
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -138,14 +138,14 @@ export default {
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="p-2 border border-solid rounded-md"
|
||||
class="p-2 border border-solid rounded-lg"
|
||||
:class="getInputErrorClass(errorMessage)"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="flex gap-1">
|
||||
<select
|
||||
v-if="groupedFilters"
|
||||
v-model="attributeKey"
|
||||
class="bg-white max-w-[30%] dark:bg-slate-900 mb-0 mr-1 text-slate-800 dark:text-slate-100 border-slate-75 dark:border-slate-600"
|
||||
class="max-w-[30%] mb-0 mr-1"
|
||||
@change="resetFilter()"
|
||||
>
|
||||
<optgroup
|
||||
@@ -166,7 +166,7 @@ export default {
|
||||
<select
|
||||
v-else
|
||||
v-model="attributeKey"
|
||||
class="bg-white max-w-[30%] dark:bg-slate-900 mb-0 mr-1 text-slate-800 dark:text-slate-100 border-slate-75 dark:border-slate-600"
|
||||
class="max-w-[30%] mb-0 mr-1"
|
||||
@change="resetFilter()"
|
||||
>
|
||||
<option
|
||||
@@ -179,10 +179,7 @@ export default {
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
v-model="filterOperator"
|
||||
class="bg-white dark:bg-slate-900 max-w-[20%] mb-0 mr-1 text-slate-800 dark:text-slate-100 border-slate-75 dark:border-slate-600"
|
||||
>
|
||||
<select v-model="filterOperator" class="max-w-[20%] mb-0 mr-1">
|
||||
<option
|
||||
v-for="(operator, o) in operators"
|
||||
:key="o"
|
||||
@@ -234,14 +231,14 @@ export default {
|
||||
v-model="values"
|
||||
type="date"
|
||||
:editable="false"
|
||||
class="mb-0 datepicker"
|
||||
class="!mb-0 datepicker"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
v-else
|
||||
v-model="values"
|
||||
type="text"
|
||||
class="mb-0"
|
||||
class="!mb-0"
|
||||
:placeholder="$t('FILTER.INPUT_PLACEHOLDER')"
|
||||
/>
|
||||
</div>
|
||||
@@ -287,7 +284,7 @@ export default {
|
||||
}
|
||||
|
||||
.filter-error {
|
||||
@apply text-red-500 dark:text-red-200 block my-1 mx-0;
|
||||
@apply text-n-ruby-9 dark:text-n-ruby-9 block my-1 mx-0;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||
import AddLabel from 'shared/components/ui/dropdown/AddLabel.vue';
|
||||
import LabelDropdown from 'shared/components/ui/label/LabelDropdown.vue';
|
||||
|
||||
const props = defineProps({
|
||||
allLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
savedLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['add', 'remove']);
|
||||
|
||||
const { isAdmin } = useAdmin();
|
||||
|
||||
const showSearchDropdownLabel = ref(false);
|
||||
|
||||
const selectedLabels = computed(() => {
|
||||
return props.savedLabels.map(label => label.title);
|
||||
});
|
||||
|
||||
const addItem = label => {
|
||||
emit('add', label);
|
||||
};
|
||||
|
||||
const removeItem = label => {
|
||||
emit('remove', label);
|
||||
};
|
||||
|
||||
const toggleLabels = () => {
|
||||
showSearchDropdownLabel.value = !showSearchDropdownLabel.value;
|
||||
};
|
||||
|
||||
const closeDropdownLabel = () => {
|
||||
showSearchDropdownLabel.value = false;
|
||||
};
|
||||
|
||||
const keyboardEvents = {
|
||||
KeyL: {
|
||||
action: e => {
|
||||
toggleLabels();
|
||||
e.preventDefault();
|
||||
},
|
||||
},
|
||||
Escape: {
|
||||
action: () => closeDropdownLabel(),
|
||||
allowOnFocusedInput: true,
|
||||
},
|
||||
};
|
||||
|
||||
useKeyboardEvents(keyboardEvents);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-on-clickaway="closeDropdownLabel" class="relative leading-6">
|
||||
<AddLabel @add="toggleLabels" />
|
||||
<woot-label
|
||||
v-for="label in savedLabels"
|
||||
:key="label.id"
|
||||
:title="label.title"
|
||||
:description="label.description"
|
||||
show-close
|
||||
:color="label.color"
|
||||
variant="smooth"
|
||||
@remove="removeItem"
|
||||
/>
|
||||
<div class="absolute w-full top-7">
|
||||
<div
|
||||
:class="{ 'dropdown-pane--open': showSearchDropdownLabel }"
|
||||
class="!box-border !w-full dropdown-pane"
|
||||
>
|
||||
<LabelDropdown
|
||||
v-if="showSearchDropdownLabel"
|
||||
:account-labels="allLabels"
|
||||
:selected-labels="selectedLabels"
|
||||
:allow-creation="isAdmin"
|
||||
@add="addItem"
|
||||
@remove="removeItem"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -14,16 +14,18 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pt-4 pb-0 px-8 border-b border-solid border-n-weak/60">
|
||||
<h2 class="text-2xl text-slate-800 dark:text-slate-100 mb-1 font-medium">
|
||||
{{ headerTitle }}
|
||||
</h2>
|
||||
<p
|
||||
v-if="headerContent"
|
||||
class="w-full text-slate-600 dark:text-slate-300 text-sm mb-2"
|
||||
>
|
||||
{{ headerContent }}
|
||||
</p>
|
||||
<slot />
|
||||
<div class="border-b border-solid border-n-weak/60">
|
||||
<div class="max-w-6xl w-full mx-auto pt-4 pb-0 px-8">
|
||||
<h2 class="text-2xl text-slate-800 dark:text-slate-100 mb-1 font-medium">
|
||||
{{ headerTitle }}
|
||||
</h2>
|
||||
<p
|
||||
v-if="headerContent"
|
||||
class="w-full text-slate-600 dark:text-slate-300 text-sm mb-2"
|
||||
>
|
||||
{{ headerContent }}
|
||||
</p>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -783,15 +783,15 @@ useEmitter(BUS_EVENTS.INSERT_INTO_RICH_EDITOR, insertContentIntoEditor);
|
||||
|
||||
.is-private {
|
||||
.prosemirror-mention-node {
|
||||
@apply font-medium bg-n-amber-2/80 dark:bg-n-amber-2/80 text-slate-900 dark:text-slate-25 py-0 px-1;
|
||||
@apply font-medium bg-n-amber-2/80 dark:bg-n-amber-2/80 text-n-slate-12 py-0 px-1;
|
||||
}
|
||||
|
||||
.ProseMirror-menubar-wrapper {
|
||||
> .ProseMirror {
|
||||
@apply text-slate-800 dark:text-slate-25;
|
||||
@apply text-n-slate-12;
|
||||
|
||||
p {
|
||||
@apply text-slate-800 dark:text-slate-25;
|
||||
@apply text-n-slate-12;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -802,11 +802,11 @@ useEmitter(BUS_EVENTS.INSERT_INTO_RICH_EDITOR, insertContentIntoEditor);
|
||||
}
|
||||
|
||||
.message-editor {
|
||||
@apply border border-solid border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-900 rounded-md py-0 px-1 mb-0;
|
||||
@apply rounded-lg outline outline-1 outline-n-weak hover:outline-n-slate-6 dark:hover:outline-n-slate-6 bg-n-alpha-black2 py-0 px-1 mb-0;
|
||||
}
|
||||
|
||||
.editor_warning {
|
||||
@apply border border-solid border-red-400 dark:border-red-400;
|
||||
@apply outline outline-1 outline-n-ruby-8 dark:outline-n-ruby-8 hover:outline-n-ruby-9 dark:hover:outline-n-ruby-9;
|
||||
}
|
||||
|
||||
.editor-warning__message {
|
||||
|
||||
@@ -1253,7 +1253,7 @@ export default {
|
||||
@apply relative py-0 px-4 -mt-px;
|
||||
|
||||
textarea {
|
||||
@apply shadow-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
|
||||
@apply shadow-none outline-none border-transparent bg-transparent m-0 max-h-60 min-h-[3rem] pt-4 pb-0 px-0 resize-none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export default {
|
||||
<woot-input
|
||||
v-model="v$.toEmailsVal.$model"
|
||||
type="text"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:!outline-none [&>input]:h-8 [&>input]:!text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
:class="{ error: v$.toEmailsVal.$error }"
|
||||
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
|
||||
@blur="onBlur"
|
||||
@@ -113,7 +113,7 @@ export default {
|
||||
<div class="flex-1 min-w-0 m-0 rounded-none whitespace-nowrap">
|
||||
<woot-input
|
||||
v-model="v$.ccEmailsVal.$model"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:!outline-none [&>input]:h-8 [&>input]:!text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
type="text"
|
||||
:class="{ error: v$.ccEmailsVal.$error }"
|
||||
:placeholder="$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.CC.PLACEHOLDER')"
|
||||
@@ -142,7 +142,7 @@ export default {
|
||||
<woot-input
|
||||
v-model="v$.bccEmailsVal.$model"
|
||||
type="text"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:!outline-none [&>input]:h-8 [&>input]:!text-sm [&>input]:!border-0 [&>input]:border-none [&>input]:!bg-transparent dark:[&>input]:!bg-transparent"
|
||||
:class="{ error: v$.bccEmailsVal.$error }"
|
||||
:placeholder="
|
||||
$t('CONVERSATION.REPLYBOX.EMAIL_HEAD.BCC.PLACEHOLDER')
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { format } from 'date-fns';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const props = defineProps({
|
||||
order: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const formatDate = dateString => {
|
||||
return format(new Date(dateString), 'MMM d, yyyy');
|
||||
};
|
||||
|
||||
const formatCurrency = (amount, currency) => {
|
||||
return new Intl.NumberFormat('en', {
|
||||
style: 'currency',
|
||||
currency: currency || 'USD',
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const getStatusClass = status => {
|
||||
const classes = {
|
||||
paid: 'bg-n-teal-5 text-n-teal-12',
|
||||
};
|
||||
return classes[status] || 'bg-slate-50 text-slate-700';
|
||||
};
|
||||
|
||||
const getStatusI18nKey = (type, status = '') => {
|
||||
return `CONVERSATION_SIDEBAR.SHOPIFY.${type.toUpperCase()}_STATUS.${status.toUpperCase()}`;
|
||||
};
|
||||
|
||||
const fulfillmentStatus = computed(() => {
|
||||
const { fulfillment_status: status } = props.order;
|
||||
if (!status) {
|
||||
return '';
|
||||
}
|
||||
return t(getStatusI18nKey('FULFILLMENT', status));
|
||||
});
|
||||
|
||||
const financialStatus = computed(() => {
|
||||
const { financial_status: status } = props.order;
|
||||
if (!status) {
|
||||
return '';
|
||||
}
|
||||
return t(getStatusI18nKey('FINANCIAL', status));
|
||||
});
|
||||
|
||||
const getFulfillmentClass = status => {
|
||||
const classes = {
|
||||
fulfilled: 'text-green-600',
|
||||
partial: 'text-yellow-600',
|
||||
unfulfilled: 'text-red-600',
|
||||
};
|
||||
return classes[status] || 'text-slate-600';
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="py-3 border-b border-n-weak last:border-b-0 flex flex-col gap-1.5"
|
||||
>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-medium flex">
|
||||
<a
|
||||
:href="order.admin_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="hover:underline text-n-slate-12 cursor-pointer truncate"
|
||||
>
|
||||
{{ $t('CONVERSATION_SIDEBAR.SHOPIFY.ORDER_ID', { id: order.id }) }}
|
||||
<i class="i-lucide-external-link pl-5" />
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
:class="getStatusClass(order.financial_status)"
|
||||
class="text-xs px-2 py-1 rounded capitalize truncate"
|
||||
:title="financialStatus"
|
||||
>
|
||||
{{ financialStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-n-slate-12">
|
||||
<span class="text-n-slate-11 border-r border-n-weak pr-2">
|
||||
{{ formatDate(order.created_at) }}
|
||||
</span>
|
||||
<span class="text-n-slate-11 pl-2">
|
||||
{{ formatCurrency(order.total_price, order.currency) }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="fulfillmentStatus">
|
||||
<span
|
||||
:class="getFulfillmentClass(order.fulfillment_status)"
|
||||
class="capitalize font-medium"
|
||||
:title="fulfillmentStatus"
|
||||
>
|
||||
{{ fulfillmentStatus }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,71 @@
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { useFunctionGetter } from 'dashboard/composables/store';
|
||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||
import ShopifyAPI from '../../../api/integrations/shopify';
|
||||
import ShopifyOrderItem from './ShopifyOrderItem.vue';
|
||||
|
||||
const props = defineProps({
|
||||
contactId: {
|
||||
type: [Number, String],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const contact = useFunctionGetter('contacts/getContact', props.contactId);
|
||||
|
||||
const hasSearchableInfo = computed(
|
||||
() => !!contact.value?.email || !!contact.value?.phone_number
|
||||
);
|
||||
|
||||
const orders = ref([]);
|
||||
const loading = ref(true);
|
||||
const error = ref('');
|
||||
|
||||
const fetchOrders = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const response = await ShopifyAPI.getOrders(props.contactId);
|
||||
orders.value = response.data.orders;
|
||||
} catch (e) {
|
||||
error.value =
|
||||
e.response?.data?.error || 'CONVERSATION_SIDEBAR.SHOPIFY.ERROR';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.contactId,
|
||||
() => {
|
||||
if (hasSearchableInfo.value) {
|
||||
fetchOrders();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="px-4 py-2 text-n-slate-12">
|
||||
<div v-if="!hasSearchableInfo" class="text-center text-n-slate-12">
|
||||
{{ $t('CONVERSATION_SIDEBAR.SHOPIFY.NO_SHOPIFY_ORDERS') }}
|
||||
</div>
|
||||
<div v-else-if="loading" class="flex justify-center items-center p-4">
|
||||
<Spinner size="32" class="text-n-brand" />
|
||||
</div>
|
||||
<div v-else-if="error" class="text-center text-n-ruby-12">
|
||||
{{ error }}
|
||||
</div>
|
||||
<div v-else-if="!orders.length" class="text-center text-n-slate-12">
|
||||
{{ $t('CONVERSATION_SIDEBAR.SHOPIFY.NO_SHOPIFY_ORDERS') }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<ShopifyOrderItem
|
||||
v-for="order in orders"
|
||||
:key="order.id"
|
||||
:order="order"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
+174
-172
@@ -166,188 +166,190 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<woot-modal
|
||||
v-model:show="show"
|
||||
full-width
|
||||
:show-close-button="false"
|
||||
:on-close="onClose"
|
||||
>
|
||||
<div
|
||||
class="bg-n-background flex flex-col h-[inherit] w-[inherit] overflow-hidden select-none"
|
||||
@click="onClose"
|
||||
<Teleport to="body">
|
||||
<woot-modal
|
||||
v-model:show="show"
|
||||
full-width
|
||||
:show-close-button="false"
|
||||
:on-close="onClose"
|
||||
>
|
||||
<header
|
||||
class="z-10 flex items-center justify-between w-full h-16 px-6 py-2 bg-n-background border-b border-n-weak"
|
||||
@click.stop
|
||||
<div
|
||||
class="bg-n-background flex flex-col h-[inherit] w-[inherit] overflow-hidden select-none"
|
||||
@click="onClose"
|
||||
>
|
||||
<div
|
||||
v-if="senderDetails"
|
||||
class="flex items-center min-w-[15rem] shrink-0"
|
||||
<header
|
||||
class="z-10 flex items-center justify-between w-full h-16 px-6 py-2 bg-n-background border-b border-n-weak"
|
||||
@click.stop
|
||||
>
|
||||
<Thumbnail
|
||||
v-if="senderDetails.avatar"
|
||||
:username="senderDetails.name"
|
||||
:src="senderDetails.avatar"
|
||||
class="flex-shrink-0"
|
||||
/>
|
||||
<div class="flex flex-col ml-2 rtl:ml-0 rtl:mr-2 overflow-hidden">
|
||||
<h3 class="text-base leading-5 m-0 font-medium">
|
||||
<span
|
||||
class="overflow-hidden text-n-slate-12 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ senderDetails.name }}
|
||||
</span>
|
||||
</h3>
|
||||
<span
|
||||
class="text-xs text-n-slate-11 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ readableTime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-1 mx-2 px-2 truncate text-sm font-medium text-center text-n-slate-12"
|
||||
>
|
||||
<span v-dompurify-html="fileNameFromDataUrl" class="truncate" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 ml-2 shrink-0">
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-zoom-in"
|
||||
slate
|
||||
ghost
|
||||
@click="onZoom(0.1)"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-zoom-out"
|
||||
slate
|
||||
ghost
|
||||
@click="onZoom(-0.1)"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-rotate-ccw"
|
||||
slate
|
||||
ghost
|
||||
@click="onRotate('counter-clockwise')"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-rotate-cw"
|
||||
slate
|
||||
ghost
|
||||
@click="onRotate('clockwise')"
|
||||
/>
|
||||
<NextButton
|
||||
icon="i-lucide-download"
|
||||
slate
|
||||
ghost
|
||||
:is-loading="isDownloading"
|
||||
:disabled="isDownloading"
|
||||
@click="onClickDownload"
|
||||
/>
|
||||
<NextButton icon="i-lucide-x" slate ghost @click="onClose" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex items-stretch flex-1 h-full overflow-hidden">
|
||||
<div class="flex items-center justify-center w-16 shrink-0">
|
||||
<NextButton
|
||||
v-if="hasMoreThanOneAttachment"
|
||||
icon="i-lucide-chevron-left"
|
||||
class="z-10"
|
||||
blue
|
||||
faded
|
||||
lg
|
||||
:disabled="activeImageIndex === 0"
|
||||
@click.stop="
|
||||
onClickChangeAttachment(
|
||||
allAttachments[activeImageIndex - 1],
|
||||
activeImageIndex - 1
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 flex items-center justify-center overflow-hidden">
|
||||
<div
|
||||
v-if="isImage"
|
||||
:style="imageWrapperStyle"
|
||||
class="flex items-center justify-center origin-center"
|
||||
:class="{
|
||||
// Adjust dimensions when rotated 90/270 degrees to maintain visibility
|
||||
// and prevent image from overflowing container in different aspect ratios
|
||||
'w-[calc(100dvh-8rem)] h-[calc(100dvw-7rem)]':
|
||||
activeImageRotation % 180 !== 0,
|
||||
'size-full': activeImageRotation % 180 === 0,
|
||||
}"
|
||||
v-if="senderDetails"
|
||||
class="flex items-center min-w-[15rem] shrink-0"
|
||||
>
|
||||
<img
|
||||
ref="imageRef"
|
||||
:key="activeAttachment.message_id"
|
||||
:src="activeAttachment.data_url"
|
||||
:style="imageStyle"
|
||||
class="max-h-full max-w-full object-contain duration-100 ease-in-out transform select-none"
|
||||
@click.stop
|
||||
@dblclick.stop="onDoubleClickZoomImage"
|
||||
@wheel.prevent.stop="onWheelImageZoom"
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
<Thumbnail
|
||||
v-if="senderDetails.avatar"
|
||||
:username="senderDetails.name"
|
||||
:src="senderDetails.avatar"
|
||||
class="flex-shrink-0"
|
||||
/>
|
||||
<div class="flex flex-col ml-2 rtl:ml-0 rtl:mr-2 overflow-hidden">
|
||||
<h3 class="text-base leading-5 m-0 font-medium">
|
||||
<span
|
||||
class="overflow-hidden text-n-slate-12 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ senderDetails.name }}
|
||||
</span>
|
||||
</h3>
|
||||
<span
|
||||
class="text-xs text-n-slate-11 whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
{{ readableTime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-1 mx-2 px-2 truncate text-sm font-medium text-center text-n-slate-12"
|
||||
>
|
||||
<span v-dompurify-html="fileNameFromDataUrl" class="truncate" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 ml-2 shrink-0">
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-zoom-in"
|
||||
slate
|
||||
ghost
|
||||
@click="onZoom(0.1)"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-zoom-out"
|
||||
slate
|
||||
ghost
|
||||
@click="onZoom(-0.1)"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-rotate-ccw"
|
||||
slate
|
||||
ghost
|
||||
@click="onRotate('counter-clockwise')"
|
||||
/>
|
||||
<NextButton
|
||||
v-if="isImage"
|
||||
icon="i-lucide-rotate-cw"
|
||||
slate
|
||||
ghost
|
||||
@click="onRotate('clockwise')"
|
||||
/>
|
||||
<NextButton
|
||||
icon="i-lucide-download"
|
||||
slate
|
||||
ghost
|
||||
:is-loading="isDownloading"
|
||||
:disabled="isDownloading"
|
||||
@click="onClickDownload"
|
||||
/>
|
||||
<NextButton icon="i-lucide-x" slate ghost @click="onClose" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex items-stretch flex-1 h-full overflow-hidden">
|
||||
<div class="flex items-center justify-center w-16 shrink-0">
|
||||
<NextButton
|
||||
v-if="hasMoreThanOneAttachment"
|
||||
icon="i-lucide-chevron-left"
|
||||
class="z-10"
|
||||
blue
|
||||
faded
|
||||
lg
|
||||
:disabled="activeImageIndex === 0"
|
||||
@click.stop="
|
||||
onClickChangeAttachment(
|
||||
allAttachments[activeImageIndex - 1],
|
||||
activeImageIndex - 1
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<video
|
||||
v-if="isVideo"
|
||||
:key="activeAttachment.message_id"
|
||||
:src="activeAttachment.data_url"
|
||||
controls
|
||||
playsInline
|
||||
class="max-h-full max-w-full object-contain"
|
||||
@click.stop
|
||||
/>
|
||||
<div class="flex-1 flex items-center justify-center overflow-hidden">
|
||||
<div
|
||||
v-if="isImage"
|
||||
:style="imageWrapperStyle"
|
||||
class="flex items-center justify-center origin-center"
|
||||
:class="{
|
||||
// Adjust dimensions when rotated 90/270 degrees to maintain visibility
|
||||
// and prevent image from overflowing container in different aspect ratios
|
||||
'w-[calc(100dvh-8rem)] h-[calc(100dvw-7rem)]':
|
||||
activeImageRotation % 180 !== 0,
|
||||
'size-full': activeImageRotation % 180 === 0,
|
||||
}"
|
||||
>
|
||||
<img
|
||||
ref="imageRef"
|
||||
:key="activeAttachment.message_id"
|
||||
:src="activeAttachment.data_url"
|
||||
:style="imageStyle"
|
||||
class="max-h-full max-w-full object-contain duration-100 ease-in-out transform select-none"
|
||||
@click.stop
|
||||
@dblclick.stop="onDoubleClickZoomImage"
|
||||
@wheel.prevent.stop="onWheelImageZoom"
|
||||
@mousemove="onMouseMove"
|
||||
@mouseleave="onMouseLeave"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<audio
|
||||
v-if="isAudio"
|
||||
:key="activeAttachment.message_id"
|
||||
controls
|
||||
class="w-full max-w-md"
|
||||
@click.stop
|
||||
>
|
||||
<source :src="`${activeAttachment.data_url}?t=${Date.now()}`" />
|
||||
</audio>
|
||||
</div>
|
||||
<video
|
||||
v-if="isVideo"
|
||||
:key="activeAttachment.message_id"
|
||||
:src="activeAttachment.data_url"
|
||||
controls
|
||||
playsInline
|
||||
class="max-h-full max-w-full object-contain"
|
||||
@click.stop
|
||||
/>
|
||||
|
||||
<div class="flex items-center justify-center w-16 shrink-0">
|
||||
<NextButton
|
||||
v-if="hasMoreThanOneAttachment"
|
||||
icon="i-lucide-chevron-right"
|
||||
class="z-10"
|
||||
blue
|
||||
faded
|
||||
lg
|
||||
:disabled="activeImageIndex === allAttachments.length - 1"
|
||||
@click.stop="
|
||||
onClickChangeAttachment(
|
||||
allAttachments[activeImageIndex + 1],
|
||||
activeImageIndex + 1
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
<audio
|
||||
v-if="isAudio"
|
||||
:key="activeAttachment.message_id"
|
||||
controls
|
||||
class="w-full max-w-md"
|
||||
@click.stop
|
||||
>
|
||||
<source :src="`${activeAttachment.data_url}?t=${Date.now()}`" />
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
<footer
|
||||
class="z-10 flex items-center justify-center h-12 border-t border-n-weak"
|
||||
>
|
||||
<div
|
||||
class="rounded-md flex items-center justify-center px-3 py-1 bg-n-slate-3 text-n-slate-12 text-sm font-medium"
|
||||
<div class="flex items-center justify-center w-16 shrink-0">
|
||||
<NextButton
|
||||
v-if="hasMoreThanOneAttachment"
|
||||
icon="i-lucide-chevron-right"
|
||||
class="z-10"
|
||||
blue
|
||||
faded
|
||||
lg
|
||||
:disabled="activeImageIndex === allAttachments.length - 1"
|
||||
@click.stop="
|
||||
onClickChangeAttachment(
|
||||
allAttachments[activeImageIndex + 1],
|
||||
activeImageIndex + 1
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer
|
||||
class="z-10 flex items-center justify-center h-12 border-t border-n-weak"
|
||||
>
|
||||
{{ `${activeImageIndex + 1} / ${allAttachments.length}` }}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</woot-modal>
|
||||
<div
|
||||
class="rounded-md flex items-center justify-center px-3 py-1 bg-n-slate-3 text-n-slate-12 text-sm font-medium"
|
||||
>
|
||||
{{ `${activeImageIndex + 1} / ${allAttachments.length}` }}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</woot-modal>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -63,7 +63,6 @@ export default {
|
||||
<label class="input-container">
|
||||
<span v-if="label">{{ label }}</span>
|
||||
<input
|
||||
class="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 border-slate-200 dark:border-slate-600"
|
||||
:value="modelValue"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
@@ -82,7 +81,7 @@ export default {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.help-text {
|
||||
@apply mt-0.5 text-xs not-italic text-slate-600 dark:text-slate-400;
|
||||
@apply mt-0.5 text-xs not-italic text-n-slate-11;
|
||||
}
|
||||
|
||||
.message {
|
||||
|
||||
@@ -175,15 +175,15 @@ export default {
|
||||
<template>
|
||||
<div class="relative phone-input--wrap">
|
||||
<div
|
||||
class="flex items-center justify-start border border-solid rounded-md dark:bg-slate-900"
|
||||
class="flex items-center justify-start border-none outline outline-1 rounded-lg bg-n-alpha-black2"
|
||||
:class="
|
||||
error
|
||||
? 'border border-solid border-red-400 dark:border-red-400 mb-1'
|
||||
: 'mb-4 border-slate-200 dark:border-slate-600'
|
||||
? 'outline-n-ruby-8 dark:outline-n-ruby-8 hover:outline-n-ruby-9 dark:hover:outline-n-ruby-9 mb-1'
|
||||
: 'mb-4 outline-n-weak dark:outline-n-weak hover:outline-n-slate-6 dark:hover:outline-n-slate-6'
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="cursor-pointer py-2 pr-1.5 pl-2 rounded-tl-md rounded-bl-md flex items-center justify-center gap-1.5 bg-slate-25 dark:bg-slate-700 h-10 w-14"
|
||||
class="cursor-pointer py-2 pr-1.5 pl-2 rounded-tl-lg rounded-bl-lg flex items-center justify-center gap-1.5 bg-n-solid-3 h-10 w-14"
|
||||
@click.prevent="toggleCountryDropdown"
|
||||
>
|
||||
<h5 v-if="activeCountry" class="mb-0">
|
||||
@@ -194,7 +194,7 @@ export default {
|
||||
</div>
|
||||
<span
|
||||
v-if="activeDialCode"
|
||||
class="flex py-2 pl-2 pr-0 text-base font-normal leading-normal bg-white dark:bg-slate-900 text-slate-800 dark:text-slate-100"
|
||||
class="flex py-2 pl-2 pr-0 text-base font-normal leading-normal text-n-slate-12"
|
||||
>
|
||||
{{ activeDialCode }}
|
||||
</span>
|
||||
@@ -202,7 +202,7 @@ export default {
|
||||
ref="phoneNumberInput"
|
||||
:value="phoneNumber"
|
||||
type="tel"
|
||||
class="!mb-0 !rounded-tl-none !rounded-bl-none !border-0 font-normal !w-full dark:!bg-slate-900 text-base !px-1.5 placeholder:font-normal"
|
||||
class="no-margin !rounded-tl-none !rounded-bl-none !outline-none !border-0 font-normal !w-full !bg-transparent text-base !px-1.5 placeholder:font-normal"
|
||||
:placeholder="placeholder"
|
||||
:readonly="readonly"
|
||||
:style="styles"
|
||||
@@ -215,20 +215,22 @@ export default {
|
||||
ref="dropdown"
|
||||
v-on-clickaway="onOutsideClick"
|
||||
tabindex="0"
|
||||
class="z-10 absolute h-60 w-[12.5rem] shadow-md overflow-y-auto top-10 rounded px-0 pt-0 pb-1 bg-white dark:bg-slate-900"
|
||||
class="z-10 absolute h-60 w-[12.5rem] shadow-md overflow-y-auto top-10 rounded-lg px-0 pt-0 pb-1 bg-n-alpha-3 backdrop-blur-[100px]"
|
||||
@keydown.prevent.up="moveUp"
|
||||
@keydown.prevent.down="moveDown"
|
||||
@keydown.prevent.enter="
|
||||
onSelectCountry(filteredCountriesBySearch[selectedIndex])
|
||||
"
|
||||
>
|
||||
<div class="sticky top-0 p-1 bg-white dark:bg-slate-900">
|
||||
<div
|
||||
class="sticky top-0 p-1 bg-white dark:bg-transparent backdrop-blur-[100px]"
|
||||
>
|
||||
<input
|
||||
ref="searchbar"
|
||||
v-model="searchCountry"
|
||||
type="text"
|
||||
:placeholder="$t('GENERAL.PHONE_INPUT.PLACEHOLDER')"
|
||||
class="!h-8 !mb-0 !text-sm !border !border-solid !border-slate-2000 dark:!border-slate-6000"
|
||||
class="!h-8 !mb-0 !text-sm !outline-n-brand dark:!outline-n-brand"
|
||||
@input="onSearchCountry"
|
||||
/>
|
||||
</div>
|
||||
@@ -236,10 +238,10 @@ export default {
|
||||
v-for="(country, index) in filteredCountriesBySearch"
|
||||
ref="dropdownItem"
|
||||
:key="index"
|
||||
class="flex items-center px-1 py-0 cursor-pointer h-7 hover:bg-slate-50 dark:hover:bg-slate-700"
|
||||
class="flex items-center px-1 py-0 cursor-pointer h-7 hover:bg-n-alpha-1 dark:hover:bg-n-alpha-2"
|
||||
:class="{
|
||||
'bg-slate-50 dark:bg-slate-700': country.id === activeCountryCode,
|
||||
'bg-slate-25 dark:bg-slate-800': index === selectedIndex,
|
||||
'bg-n-alpha-1 dark:bg-n-alpha-2': country.id === activeCountryCode,
|
||||
'bg-n-alpha-1 dark:bg-n-alpha-2': index === selectedIndex,
|
||||
}"
|
||||
@click="onSelectCountry(country)"
|
||||
>
|
||||
@@ -250,13 +252,13 @@ export default {
|
||||
>
|
||||
{{ country.name }}
|
||||
</span>
|
||||
<span class="ml-1 text-xs text-slate-300 dark:text-slate-300">{{
|
||||
country.dial_code
|
||||
}}</span>
|
||||
<span class="ml-1 text-xs text-n-slate-11">
|
||||
{{ country.dial_code }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="filteredCountriesBySearch.length === 0">
|
||||
<span
|
||||
class="flex items-center justify-center mt-4 text-sm text-slate-500 dark:text-slate-300"
|
||||
class="flex items-center justify-center mt-4 text-sm text-n-slate-10"
|
||||
>
|
||||
{{ $t('GENERAL.PHONE_INPUT.EMPTY_STATE') }}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user