feat: Replace vue-datepicker-next from codebase

This commit is contained in:
iamsivin
2026-02-19 20:27:43 +05:30
parent 2f21009609
commit 68771ce928
30 changed files with 718 additions and 246 deletions
@@ -12,9 +12,6 @@
// Base styles for elements
@import 'base';
// Plugins
@import 'plugins/date-picker';
html,
body {
font-family:
@@ -0,0 +1,183 @@
<script setup>
import { ref, computed } from 'vue';
import {
startOfMonth,
addMonths,
subMonths,
startOfDay,
isSameDay,
addHours,
addYears,
subYears,
setMonth,
setYear,
} from 'date-fns';
import { CALENDAR_TYPES, CALENDAR_PERIODS } from './helpers/DatePickerHelper';
import CalendarYear from './components/CalendarYear.vue';
import CalendarMonth from './components/CalendarMonth.vue';
import CalendarWeek from './components/CalendarWeek.vue';
import CalendarFooter from './components/CalendarFooter.vue';
import TimePicker from './components/TimePicker.vue';
defineProps({
minDate: {
type: Date,
default: null,
},
});
const emit = defineEmits(['apply', 'clear']);
const { START_CALENDAR } = CALENDAR_TYPES;
const { WEEK, MONTH, YEAR } = CALENDAR_PERIODS;
const currentDate = ref(new Date());
const selectedDate = ref(null);
const getCurrentTime = () => {
const now = new Date();
return {
hour: now.getHours(),
minute: now.getMinutes(),
second: now.getSeconds(),
};
};
const selectedTime = ref(getCurrentTime());
const calendarView = ref(WEEK);
const calendarDate = ref(startOfMonth(currentDate.value));
const getMinTimeForToday = () => addHours(new Date(), 1);
const minTime = computed(() => {
if (!selectedDate.value) return null;
if (!isSameDay(selectedDate.value, currentDate.value)) return null;
return getMinTimeForToday();
});
const selectedDateTime = computed(() => {
if (!selectedDate.value) return null;
const date = new Date(selectedDate.value);
date.setHours(
selectedTime.value.hour,
selectedTime.value.minute,
selectedTime.value.second,
0
);
return date;
});
const selectDate = day => {
selectedDate.value = startOfDay(day);
if (isSameDay(day, currentDate.value)) {
const minDate = getMinTimeForToday();
selectedTime.value = {
hour: minDate.getHours(),
minute: minDate.getMinutes(),
second: 0,
};
} else {
selectedTime.value = { hour: 0, minute: 0, second: 0 };
}
};
const moveCalendar = (direction, period = MONTH) => {
const adjust =
period === YEAR
? { prev: subYears, next: addYears }
: { prev: subMonths, next: addMonths };
calendarDate.value = adjust[direction](calendarDate.value, 1);
};
const setViewMode = (_calendar, mode) => {
calendarView.value = mode;
};
const openCalendar = (index, _calendarType, period = MONTH) => {
calendarDate.value =
period === MONTH
? setMonth(startOfMonth(calendarDate.value), index)
: setYear(calendarDate.value, index);
calendarView.value = period === MONTH ? WEEK : MONTH;
};
const onApply = () => {
if (selectedDateTime.value) {
emit('apply', selectedDateTime.value);
}
};
const isDefaultState = computed(
() => !selectedDate.value && calendarView.value === WEEK
);
const resetState = () => {
selectedDate.value = null;
selectedTime.value = getCurrentTime();
calendarDate.value = startOfMonth(currentDate.value);
calendarView.value = WEEK;
};
const onClear = () => {
if (isDefaultState.value) {
emit('clear');
return;
}
resetState();
};
defineExpose({ resetState });
</script>
<template>
<div class="flex flex-col select-none font-inter">
<div class="flex w-full gap-3 justify-between">
<div class="flex justify-center py-5">
<div class="flex flex-col items-center gap-2 min-w-[300px]">
<CalendarYear
v-if="calendarView === YEAR"
:calendar-type="START_CALENDAR"
:start-current-date="calendarDate"
:end-current-date="addMonths(calendarDate, 1)"
@select-year="openCalendar($event, START_CALENDAR, YEAR)"
/>
<CalendarMonth
v-else-if="calendarView === MONTH"
:calendar-type="START_CALENDAR"
:start-current-date="calendarDate"
:end-current-date="addMonths(calendarDate, 1)"
@select-month="openCalendar($event, START_CALENDAR)"
@set-view="setViewMode"
@prev="moveCalendar('prev', YEAR)"
@next="moveCalendar('next', YEAR)"
/>
<CalendarWeek
v-else
:calendar-type="START_CALENDAR"
:current-date="currentDate"
:start-current-date="calendarDate"
:end-current-date="addMonths(calendarDate, 1)"
:selected-start-date="selectedDate"
:selected-end-date="selectedDate"
:selecting-end-date="false"
:hovered-end-date="null"
:min-date="minDate"
@select-date="selectDate"
@set-view="setViewMode"
@prev="moveCalendar('prev')"
@next="moveCalendar('next')"
/>
</div>
</div>
<div
class="flex justify-center py-2 w-full transition-opacity ltr:border-l rtl:border-r border-n-strong"
:class="selectedDate ? 'opacity-100' : 'opacity-40 pointer-events-none'"
>
<TimePicker v-model="selectedTime" :min-time="minTime" />
</div>
</div>
<div class="border-t border-n-strong">
<CalendarFooter @change="onApply" @clear="onClear" />
</div>
</div>
</template>
@@ -1,4 +1,5 @@
<script setup>
import { startOfDay } from 'date-fns';
import {
monthName,
yearName,
@@ -28,6 +29,10 @@ const props = defineProps({
selectingEndDate: Boolean,
selectedEndDate: Date,
hoveredEndDate: Date,
minDate: {
type: Date,
default: null,
},
});
const emit = defineEmits([
@@ -41,11 +46,18 @@ const emit = defineEmits([
const { START_CALENDAR } = CALENDAR_TYPES;
const { MONTH } = CALENDAR_PERIODS;
const isDayDisabled = day => {
if (!props.minDate) return false;
return startOfDay(day) < startOfDay(props.minDate);
};
const emitHoveredEndDate = day => {
if (isDayDisabled(day)) return;
emit('updateHoveredEndDate', day);
};
const emitSelectDate = day => {
if (isDayDisabled(day)) return;
emit('selectDate', day);
};
const onClickPrev = () => {
@@ -108,8 +120,10 @@ const isNextDayInRange = day => {
const dayClasses = day => ({
'text-n-slate-10 pointer-events-none': !isInCurrentMonth(day),
'text-n-slate-10 pointer-events-none opacity-40':
isInCurrentMonth(day) && isDayDisabled(day),
'text-n-slate-12 hover:text-n-slate-12 hover:bg-n-blue-6 dark:hover:bg-n-blue-7':
isInCurrentMonth(day),
isInCurrentMonth(day) && !isDayDisabled(day),
'bg-n-brand text-white':
isSelectedStartOrEndDate(day) && isInCurrentMonth(day),
'bg-n-blue-4 dark:bg-n-blue-5':
@@ -0,0 +1,430 @@
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import TabBar from 'dashboard/components-next/tabbar/TabBar.vue';
import {
TIME_COLUMNS,
TIME_PERIODS,
TIME_FORMATS,
} from '../helpers/DatePickerHelper';
const props = defineProps({
minTime: { type: Date, default: null },
});
const model = defineModel({
type: Object,
default: () => ({ hour: 9, minute: 0, second: 0 }),
});
const { t } = useI18n();
const { HOUR, HOUR_12, MINUTE, SECOND, PERIOD } = TIME_COLUMNS;
const { AM, PM } = TIME_PERIODS;
const { H24, H12 } = TIME_FORMATS;
const ITEM_HEIGHT = 40;
const CENTER_ROW = 2;
const VISIBLE_ROWS = 5;
const SNAP_DELAY = 120;
const SEPARATOR = ':';
const is24HourFormat = ref(false);
const selectedTime = reactive({
hour: model.value.hour,
minute: model.value.minute,
second: model.value.second ?? 0,
});
const activeIndex = reactive({});
const dragOffset = reactive({});
const interactionMode = reactive({});
const touchStartY = {};
const snapTimers = {};
const columnRefs = reactive({});
const focusedColumn = ref(null);
const period = computed(() => (selectedTime.hour >= 12 ? PM : AM));
const showPeriod = computed(() => !is24HourFormat.value);
const minBound = computed(() => {
if (!props.minTime) return null;
const d = props.minTime;
return { h: d.getHours(), m: d.getMinutes(), s: d.getSeconds() };
});
const toTimeNumber = (h, m, s) => h * 3600 + m * 60 + s;
const convertTo12Hour = hour => {
if (hour === 0) return 12;
return hour > 12 ? hour - 12 : hour;
};
const convertTo24Hour = (hour12, timePeriod) => {
if (timePeriod === AM) return hour12 === 12 ? 0 : hour12;
return hour12 === 12 ? 12 : hour12 + 12;
};
const isItemDisabled = (key, val) => {
const mb = minBound.value;
if (!mb) return false;
const minVal = toTimeNumber(mb.h, mb.m, mb.s);
const { hour, minute } = selectedTime;
if (key === HOUR) return toTimeNumber(val, 0, 0) < toTimeNumber(mb.h, 0, 0);
if (key === MINUTE) return toTimeNumber(hour, val, 0) < minVal;
if (key === SECOND) return toTimeNumber(hour, minute, val) < minVal;
return false;
};
const formatLabel = value => String(value).padStart(2, '0');
const buildItems = (count, key) =>
Array.from({ length: count }, (_, i) => ({
value: i,
label: formatLabel(i),
disabled: isItemDisabled(key, i),
}));
const buildHour12Items = () =>
Array.from({ length: 12 }, (_, i) => {
const display = i === 0 ? 12 : i;
return {
value: display,
label: formatLabel(display),
disabled: isItemDisabled(HOUR, convertTo24Hour(display, period.value)),
};
});
const periodItems = computed(() => [
{ value: AM, label: AM, disabled: minBound.value?.h >= 12 },
{ value: PM, label: PM, disabled: false },
]);
const columns = computed(() => [
{
key: is24HourFormat.value ? HOUR : HOUR_12,
label: t('DATE_PICKER.HOUR'),
items: is24HourFormat.value ? buildItems(24, HOUR) : buildHour12Items(),
},
{
key: MINUTE,
label: t('DATE_PICKER.MINUTE'),
items: buildItems(60, MINUTE),
},
{
key: SECOND,
label: t('DATE_PICKER.SECOND'),
items: buildItems(60, SECOND),
},
{ key: PERIOD, label: '', items: periodItems.value },
]);
const findColumn = key => columns.value.find(c => c.key === key);
const findNearestEnabled = (items, index) => {
for (let d = 0; d < items.length; d += 1) {
if (index + d < items.length && !items[index + d].disabled)
return index + d;
if (index - d >= 0 && !items[index - d].disabled) return index - d;
}
return index;
};
const getActiveIndex = key => {
if (key === HOUR) return selectedTime.hour;
if (key === HOUR_12) {
const h = convertTo12Hour(selectedTime.hour);
return h === 12 ? 0 : h;
}
if (key === PERIOD) return period.value === AM ? 0 : 1;
return selectedTime[key];
};
const getColumnTranslateY = key => {
const idx = -(activeIndex[key] ?? 0) * ITEM_HEIGHT;
return CENTER_ROW * ITEM_HEIGHT + idx + (dragOffset[key] ?? 0);
};
const getMaxDragOffset = key => {
const column = findColumn(key);
if (!column) return { maxDown: 0, maxUp: 0 };
const currentIdx = activeIndex[key] ?? 0;
const firstEnabled = column.items.findIndex(i => !i.disabled);
const lastEnabled = column.items.findLastIndex(i => !i.disabled);
if (firstEnabled === -1) return { maxDown: 0, maxUp: 0 };
return {
maxDown: (currentIdx - firstEnabled) * ITEM_HEIGHT,
maxUp: (lastEnabled - currentIdx) * ITEM_HEIGHT,
};
};
const clampDragOffset = (key, rawOffset) => {
const { maxDown, maxUp } = getMaxDragOffset(key);
return Math.max(-maxUp, Math.min(maxDown, rawOffset));
};
let lastEmittedSignature = '';
const emitTimeValue = () => {
const sig = `${selectedTime.hour}:${selectedTime.minute}:${selectedTime.second}`;
if (sig === lastEmittedSignature) return;
lastEmittedSignature = sig;
model.value = { ...selectedTime };
};
const applySelection = (key, item) => {
if (key === HOUR_12) {
selectedTime.hour = convertTo24Hour(item.value, period.value);
} else if (key === PERIOD) {
selectedTime.hour = convertTo24Hour(
convertTo12Hour(selectedTime.hour),
item.value
);
} else {
selectedTime[key] = item.value;
}
emitTimeValue();
};
const selectIndex = (key, targetIndex) => {
const column = findColumn(key);
if (!column) return;
const clamped = Math.max(0, Math.min(targetIndex, column.items.length - 1));
const resolved = column.items[clamped]?.disabled
? findNearestEnabled(column.items, clamped)
: clamped;
activeIndex[key] = resolved;
const item = column.items[resolved];
if (item) applySelection(key, item);
};
const selectByValue = (key, value) => {
const column = findColumn(key);
if (!column) return;
const idx = column.items.findIndex(i => i.value === value && !i.disabled);
if (idx !== -1) selectIndex(key, idx);
};
const snapToNearest = key => {
const offset = dragOffset[key] ?? 0;
const steps = Math.round(-offset / ITEM_HEIGHT);
dragOffset[key] = 0;
interactionMode[key] = null;
if (steps !== 0) selectIndex(key, (activeIndex[key] ?? 0) + steps);
};
const isItemSelected = (key, val) => {
if (key === HOUR_12) return convertTo12Hour(selectedTime.hour) === val;
if (key === PERIOD) return period.value === val;
return selectedTime[key] === val;
};
const isColumnHidden = key => key === PERIOD && !showPeriod.value;
const onWheelScroll = (key, event) => {
event.preventDefault();
interactionMode[key] = 'wheel';
dragOffset[key] = clampDragOffset(key, (dragOffset[key] ?? 0) - event.deltaY);
clearTimeout(snapTimers[key]);
snapTimers[key] = setTimeout(() => snapToNearest(key), SNAP_DELAY);
};
const onTouchStart = (key, event) => {
interactionMode[key] = 'touch';
touchStartY[key] = event.touches[0].clientY;
dragOffset[key] = 0;
};
const onTouchMove = (key, event) => {
event.preventDefault();
dragOffset[key] = clampDragOffset(
key,
event.touches[0].clientY - touchStartY[key]
);
};
const onTouchEnd = key => snapToNearest(key);
const visibleColumnKeys = computed(() =>
columns.value.filter(col => !isColumnHidden(col.key)).map(col => col.key)
);
const onKeyDown = (key, event) => {
const { key: pressed, shiftKey } = event;
if (pressed === 'ArrowUp' || pressed === 'ArrowDown') {
event.preventDefault();
selectIndex(
key,
(activeIndex[key] ?? 0) + (pressed === 'ArrowDown' ? 1 : -1)
);
return;
}
if (pressed === 'Tab') {
const keys = visibleColumnKeys.value;
const next = keys.indexOf(key) + (shiftKey ? -1 : 1);
if (next >= 0 && next < keys.length) {
event.preventDefault();
columnRefs[keys[next]]?.focus();
}
}
};
const syncAllColumns = () => {
columns.value.forEach(col => {
activeIndex[col.key] = getActiveIndex(col.key);
});
};
const formatTabs = [
{ label: t('DATE_PICKER.FORMAT_24H'), value: H24 },
{ label: t('DATE_PICKER.FORMAT_12H'), value: H12 },
];
const activeFormatTab = computed(() => (is24HourFormat.value ? 0 : 1));
const onFormatChange = tab => {
is24HourFormat.value = tab.value === H24;
syncAllColumns();
};
watch(model, v => {
selectedTime.hour = v.hour;
selectedTime.minute = v.minute;
selectedTime.second = v.second ?? 0;
syncAllColumns();
});
onMounted(syncAllColumns);
</script>
<template>
<div class="relative flex flex-col items-center py-2 w-full">
<div class="mb-4">
<TabBar
:tabs="formatTabs"
:initial-active-tab="activeFormatTab"
@tab-changed="onFormatChange"
/>
</div>
<div class="flex items-center z-[1] mb-1">
<template v-for="(col, colIdx) in columns" :key="`label-${col.key}`">
<span
class="text-center text-xs font-medium text-n-slate-11 transition-all duration-300 ease-in-out overflow-hidden"
:class="
isColumnHidden(col.key) ? 'w-0 opacity-0' : 'w-14 opacity-100'
"
>
{{ col.label }}
</span>
<span
v-if="colIdx < columns.length - 1"
class="text-lg font-520 text-transparent transition-all duration-300 ease-in-out overflow-hidden"
:class="
isColumnHidden(columns[colIdx + 1]?.key)
? 'w-0 opacity-0'
: 'opacity-100'
"
>
{{ SEPARATOR }}
</span>
</template>
</div>
<div class="relative w-full">
<div
class="absolute inset-x-6 h-10 rounded-2xl bg-n-solid-active outline outline-1 -outline-offset-1 outline-n-weak pointer-events-none shadow-inner"
:style="{ top: `${CENTER_ROW * ITEM_HEIGHT}px` }"
/>
<div class="flex items-center justify-center z-[1] relative">
<template v-for="(col, colIdx) in columns" :key="col.key">
<div
:ref="
el => {
if (el) columnRefs[col.key] = el;
}
"
:tabindex="isColumnHidden(col.key) ? -1 : 0"
class="time-wheel relative overflow-hidden transition-all duration-300 ease-in-out outline-none"
:class="
isColumnHidden(col.key) ? 'w-0 opacity-0' : 'w-14 opacity-100'
"
:style="{ height: `${VISIBLE_ROWS * ITEM_HEIGHT}px` }"
@wheel.prevent="onWheelScroll(col.key, $event)"
@touchstart="onTouchStart(col.key, $event)"
@touchmove.prevent="onTouchMove(col.key, $event)"
@touchend="onTouchEnd(col.key)"
@keydown="onKeyDown(col.key, $event)"
@focus="focusedColumn = col.key"
@blur="focusedColumn = null"
>
<div
class="flex flex-col items-center"
:class="{
'transition-transform duration-200 ease-out':
!interactionMode[col.key],
'transition-transform duration-100 ease-out':
interactionMode[col.key] === 'wheel',
}"
:style="{
transform: `translateY(${getColumnTranslateY(col.key)}px)`,
}"
>
<button
v-for="item in col.items"
:key="item.value"
:disabled="item.disabled"
tabindex="-1"
class="flex items-center justify-center w-14 shrink-0 text-base font-semibold transition-colors outline-none"
:style="{ height: `${ITEM_HEIGHT}px` }"
:class="[
isItemSelected(col.key, item.value)
? 'text-n-slate-12'
: 'text-n-slate-9',
item.disabled
? 'opacity-20 cursor-not-allowed'
: 'cursor-pointer',
isItemSelected(col.key, item.value) &&
focusedColumn === col.key
? 'outline outline-1 outline-n-brand -outline-offset-4 rounded-xl'
: '',
]"
@click="selectByValue(col.key, item.value)"
>
{{ item.label }}
</button>
</div>
</div>
<span
v-if="colIdx < columns.length - 1 && col.key !== SECOND"
class="text-lg font-520 text-n-slate-11"
>
{{ SEPARATOR }}
</span>
</template>
</div>
</div>
</div>
</template>
<style scoped>
.time-wheel {
mask-image: linear-gradient(
to bottom,
transparent 0%,
rgba(0, 0, 0, 0.3) 15%,
rgba(0, 0, 0, 0.7) 30%,
black 40%,
black 60%,
rgba(0, 0, 0, 0.7) 70%,
rgba(0, 0, 0, 0.3) 85%,
transparent 100%
);
-webkit-mask-image: linear-gradient(
to bottom,
transparent 0%,
rgba(0, 0, 0, 0.3) 15%,
rgba(0, 0, 0, 0.7) 30%,
black 40%,
black 60%,
rgba(0, 0, 0, 0.7) 70%,
rgba(0, 0, 0, 0.3) 85%,
transparent 100%
);
}
</style>
@@ -81,6 +81,24 @@ export const CALENDAR_PERIODS = {
YEAR: 'year',
};
export const TIME_COLUMNS = {
HOUR: 'hour',
HOUR_12: 'hour12',
MINUTE: 'minute',
SECOND: 'second',
PERIOD: 'period',
};
export const TIME_PERIODS = {
AM: 'AM',
PM: 'PM',
};
export const TIME_FORMATS = {
H24: '24h',
H12: '12h',
};
// Utility functions for date operations
export const monthName = currentDate => format(currentDate, 'MMMM');
export const yearName = currentDate => format(currentDate, 'yyyy');
@@ -107,7 +107,7 @@ defineExpose({ open, close });
<TeleportWithDirection to="body">
<dialog
ref="dialogRef"
class="w-full transition-all duration-300 ease-in-out shadow-xl rounded-xl"
class="w-full transition-all duration-300 ease-in-out shadow-xl rounded-xl focus-within:outline-none focus-within:outline-0"
:class="[
maxWidthClass,
positionClass,
@@ -1,77 +1,52 @@
<script>
import DatePicker from 'vue-datepicker-next';
import NextButton from 'dashboard/components-next/button/Button.vue';
<script setup>
import { ref } from 'vue';
import Dialog from 'dashboard/components-next/dialog/Dialog.vue';
import DatePicker from 'dashboard/components-next/DatePicker/DatePicker.vue';
export default {
components: {
DatePicker,
NextButton,
},
emits: ['close', 'chooseTime'],
const emit = defineEmits(['chooseTime']);
data() {
return {
snoozeTime: null,
lang: {
days: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
yearFormat: 'YYYY',
monthFormat: 'MMMM',
},
};
},
const dialogRef = ref(null);
const datePickerRef = ref(null);
const today = new Date();
methods: {
onClose() {
this.$emit('close');
},
chooseTime() {
this.$emit('chooseTime', this.snoozeTime);
},
disabledDate(date) {
// Disable all the previous dates
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
return date < yesterday;
},
disabledTime(date) {
// Allow only time after 1 hour
const now = new Date();
now.setHours(now.getHours() + 1);
return date < now;
},
},
const onApply = dateTime => {
dialogRef.value?.close();
emit('chooseTime', dateTime);
};
const onClear = () => {
dialogRef.value?.close();
};
const onDialogClose = () => {
datePickerRef.value?.resetState();
};
const open = () => {
dialogRef.value?.open();
};
const close = () => {
dialogRef.value?.close();
};
defineExpose({ open, close });
</script>
<template>
<div class="flex flex-col">
<woot-modal-header :header-title="$t('CONVERSATION.CUSTOM_SNOOZE.TITLE')" />
<form
class="modal-content w-full pt-2 px-5 pb-6"
@submit.prevent="chooseTime"
>
<DatePicker
v-model:value="snoozeTime"
type="datetime"
inline
input-class="mx-input "
:lang="lang"
:disabled-date="disabledDate"
:disabled-time="disabledTime"
/>
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
<NextButton
faded
slate
type="reset"
:label="$t('CONVERSATION.CUSTOM_SNOOZE.CANCEL')"
@click.prevent="onClose"
/>
<NextButton
type="submit"
:label="$t('CONVERSATION.CUSTOM_SNOOZE.APPLY')"
/>
</div>
</form>
</div>
<Dialog
ref="dialogRef"
:title="$t('CONVERSATION.CUSTOM_SNOOZE.TITLE')"
:show-confirm-button="false"
:show-cancel-button="false"
width="2xl"
@close="onDialogClose"
>
<DatePicker
ref="datePickerRef"
:min-date="today"
@apply="onApply"
@clear="onClear"
/>
</Dialog>
</template>
@@ -17,7 +17,6 @@ import Modal from './Modal.vue';
import Spinner from 'shared/components/Spinner.vue';
import Tabs from './ui/Tabs/Tabs.vue';
import TabsItem from './ui/Tabs/TabsItem.vue';
import DatePicker from './ui/DatePicker/DatePicker.vue';
const WootUIKit = {
Code,
@@ -37,7 +36,6 @@ const WootUIKit = {
Spinner,
Tabs,
TabsItem,
DatePicker,
install(Vue) {
const keys = Object.keys(this);
keys.pop(); // remove 'install' from keys
@@ -1,41 +0,0 @@
<script>
import DatePicker from 'vue-datepicker-next';
export default {
components: { DatePicker },
props: {
confirmText: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
value: {
type: Array,
default: () => [],
},
},
emits: ['change'],
methods: {
handleChange(value) {
this.$emit('change', value);
},
},
};
</script>
<template>
<div class="date-picker">
<DatePicker
range
confirm
:clearable="false"
:editable="false"
:confirm-text="confirmText"
:placeholder="placeholder"
:value="value"
@change="handleChange"
/>
</div>
</template>
@@ -1,48 +0,0 @@
<script>
import addDays from 'date-fns/addDays';
import DatePicker from 'vue-datepicker-next';
export default {
components: { DatePicker },
props: {
confirmText: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
value: {
type: Date,
default: [],
},
},
emits: ['change'],
methods: {
handleChange(value) {
this.$emit('change', value);
},
disableBeforeToday(date) {
const yesterdayDate = addDays(new Date(), -1);
return date < yesterdayDate;
},
},
};
</script>
<template>
<div class="date-picker">
<DatePicker
type="datetime"
confirm
:clearable="false"
:editable="false"
:confirm-text="confirmText"
:placeholder="placeholder"
:value="value"
:disabled-date="disableBeforeToday"
@change="handleChange"
/>
</div>
</template>
@@ -65,7 +65,6 @@ export default {
showLabelActions: false,
showTeamsList: false,
popoverPositions: {},
showCustomTimeSnoozeModal: false,
};
},
mounted() {
@@ -99,7 +98,7 @@ export default {
methods: {
onCmdSnoozeConversation(snoozeType) {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
this.showCustomTimeSnoozeModal = true;
this.$refs.snoozeModalRef?.open();
} else if (typeof snoozeType === 'number') {
this.updateConversations('snoozed', snoozeType);
} else {
@@ -113,14 +112,10 @@ export default {
this.updateConversations('resolved', null);
},
customSnoozeTime(customSnoozedTime) {
this.showCustomTimeSnoozeModal = false;
if (customSnoozedTime) {
this.updateConversations('snoozed', getUnixTime(customSnoozedTime));
}
},
hideCustomSnoozeModal() {
this.showCustomTimeSnoozeModal = false;
},
selectAll(e) {
this.$emit('selectAllConversations', e.target.checked);
},
@@ -251,15 +246,7 @@ export default {
<div v-if="allConversationsSelected" class="bulk-action__alert">
{{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }}
</div>
<woot-modal
v-model:show="showCustomTimeSnoozeModal"
:on-close="hideCustomSnoozeModal"
>
<CustomSnoozeModal
@close="hideCustomSnoozeModal"
@choose-time="customSnoozeTime"
/>
</woot-modal>
<CustomSnoozeModal ref="snoozeModalRef" @choose-time="customSnoozeTime" />
</div>
</template>
@@ -5,6 +5,11 @@
"WEEK_NUMBER": "Week #{weekNumber}",
"APPLY_BUTTON": "Apply",
"CLEAR_BUTTON": "Clear",
"HOUR": "Hour",
"MINUTE": "Min",
"SECOND": "Sec",
"FORMAT_12H": "12h",
"FORMAT_24H": "24h",
"DATE_RANGE_INPUT": {
"START": "Start Date",
"END": "End Date"
@@ -13,7 +13,7 @@ import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
const store = useStore();
const getters = useStoreGetters();
const { t } = useI18n();
const showCustomSnoozeModal = ref(false);
const snoozeModalRef = ref(null);
const selectedChat = computed(() => getters.getSelectedChat.value);
const contextMenuChatId = computed(() => getters.getContextMenuChatId.value);
@@ -30,7 +30,7 @@ const toggleStatus = async (status, snoozedUntil) => {
const onCmdSnoozeConversation = snoozeType => {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
showCustomSnoozeModal.value = true;
snoozeModalRef.value?.open();
} else if (typeof snoozeType === 'number') {
toggleStatus(wootConstants.STATUS_TYPE.SNOOZED, snoozeType);
} else {
@@ -42,7 +42,7 @@ const onCmdSnoozeConversation = snoozeType => {
};
const chooseSnoozeTime = customSnoozeTime => {
showCustomSnoozeModal.value = false;
store.dispatch('setContextMenuChatId', null);
if (customSnoozeTime) {
toggleStatus(
wootConstants.STATUS_TYPE.SNOOZED,
@@ -51,24 +51,9 @@ const chooseSnoozeTime = customSnoozeTime => {
}
};
const hideCustomSnoozeModal = () => {
// if we select custom snooze and the custom snooze modal is open
// Then if the custom snooze modal is closed then set the context menu chat id to null
store.dispatch('setContextMenuChatId', null);
showCustomSnoozeModal.value = false;
};
useEmitter(CMD_SNOOZE_CONVERSATION, onCmdSnoozeConversation);
</script>
<template>
<woot-modal
v-model:show="showCustomSnoozeModal"
:on-close="hideCustomSnoozeModal"
>
<CustomSnoozeModal
@close="hideCustomSnoozeModal"
@choose-time="chooseSnoozeTime"
/>
</woot-modal>
<CustomSnoozeModal ref="snoozeModalRef" @choose-time="chooseSnoozeTime" />
</template>
@@ -34,9 +34,6 @@ export default {
},
},
emits: ['next', 'prev'],
data() {
return { showCustomSnoozeModal: false };
},
computed: {
...mapGetters({ meta: 'notifications/getMeta' }),
},
@@ -51,9 +48,6 @@ export default {
const ninja = document.querySelector('ninja-keys');
ninja.open({ parent: 'snooze_notification' });
},
hideCustomSnoozeModal() {
this.showCustomSnoozeModal = false;
},
async snoozeNotification(snoozedUntil) {
try {
await this.$store.dispatch('notifications/snooze', {
@@ -68,7 +62,7 @@ export default {
},
onCmdSnoozeNotification(snoozeType) {
if (snoozeType === wootConstants.SNOOZE_OPTIONS.UNTIL_CUSTOM_TIME) {
this.showCustomSnoozeModal = true;
this.$refs.snoozeModalRef?.open();
} else if (typeof snoozeType === 'number') {
this.snoozeNotification(snoozeType);
} else {
@@ -77,7 +71,6 @@ export default {
}
},
scheduleCustomSnooze(customSnoozeTime) {
this.showCustomSnoozeModal = false;
if (customSnoozeTime) {
const snoozedUntil = getUnixTime(customSnoozeTime) || null;
this.snoozeNotification(snoozedUntil);
@@ -147,14 +140,9 @@ export default {
@click="deleteNotification"
/>
</div>
<woot-modal
v-model:show="showCustomSnoozeModal"
:on-close="hideCustomSnoozeModal"
>
<CustomSnoozeModal
@close="hideCustomSnoozeModal"
@choose-time="scheduleCustomSnooze"
/>
</woot-modal>
<CustomSnoozeModal
ref="snoozeModalRef"
@choose-time="scheduleCustomSnooze"
/>
</div>
</template>
@@ -14,13 +14,13 @@ import {
import FilterButton from 'dashboard/components/ui/Dropdown/DropdownButton.vue';
import ActiveFilterChip from '../Filters/v3/ActiveFilterChip.vue';
import AddFilterChip from '../Filters/v3/AddFilterChip.vue';
import WootDatePicker from 'dashboard/components/ui/DatePicker/DatePicker.vue';
import DateRangePicker from 'dashboard/components-next/DatePicker/DateRangePicker.vue';
import {
parseReportURLParams,
parseFilterURLParams,
generateCompleteURLParams,
} from '../../helpers/reportFilterHelper';
import { DATE_RANGE_TYPES } from 'dashboard/components/ui/DatePicker/helpers/DatePickerHelper';
import { DATE_RANGE_TYPES } from 'dashboard/components-next/DatePicker/helpers/DatePickerHelper.js';
const props = defineProps({
showTeamFilter: {
@@ -254,7 +254,7 @@ onMounted(() => {
<template>
<div class="flex flex-col flex-wrap w-full gap-3 md:flex-row">
<WootDatePicker
<DateRangePicker
v-model:date-range="customDateRange"
v-model:range-type="selectedDateRange"
@date-range-changed="onDateRangeChange"
@@ -3,13 +3,13 @@ import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { getUnixStartOfDay, getUnixEndOfDay } from 'helpers/DateHelper';
import subDays from 'date-fns/subDays';
import WootDatePicker from 'dashboard/components/ui/DatePicker/DatePicker.vue';
import DateRangePicker from 'dashboard/components-next/DatePicker/DateRangePicker.vue';
import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
import {
generateReportURLParams,
parseReportURLParams,
} from '../helpers/reportFilterHelper';
import { DATE_RANGE_TYPES } from 'dashboard/components/ui/DatePicker/helpers/DatePickerHelper';
import { DATE_RANGE_TYPES } from 'dashboard/components-next/DatePicker/helpers/DatePickerHelper.js';
defineProps({
disabled: {
@@ -91,7 +91,7 @@ onMounted(() => {
:class="{ 'pointer-events-none opacity-50': disabled }"
>
<div class="flex flex-col flex-wrap items-start gap-2 md:flex-row">
<WootDatePicker
<DateRangePicker
v-model:date-range="customDateRange"
v-model:range-type="selectedDateRange"
@date-range-changed="onDateRangeChange"
@@ -7,10 +7,10 @@ import { getUnixStartOfDay, getUnixEndOfDay } from 'helpers/DateHelper';
import subDays from 'date-fns/subDays';
import differenceInDays from 'date-fns/differenceInDays';
import ActiveFilterChip from './Filters/v3/ActiveFilterChip.vue';
import WootDatePicker from 'dashboard/components/ui/DatePicker/DatePicker.vue';
import DateRangePicker from 'dashboard/components-next/DatePicker/DateRangePicker.vue';
import ToggleSwitch from 'dashboard/components-next/switch/Switch.vue';
import { GROUP_BY_FILTER } from '../constants';
import { DATE_RANGE_TYPES } from 'dashboard/components/ui/DatePicker/helpers/DatePickerHelper';
import { DATE_RANGE_TYPES } from 'dashboard/components-next/DatePicker/helpers/DatePickerHelper.js';
import {
generateReportURLParams,
parseReportURLParams,
@@ -320,7 +320,7 @@ onMounted(() => {
<template>
<div class="flex flex-col w-full gap-3 lg:flex-row">
<WootDatePicker
<DateRangePicker
v-model:date-range="customDateRange"
v-model:range-type="selectedDateRange"
@date-range-changed="onDateRangeChange"
@@ -2,7 +2,7 @@
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import SLAFilter from '../SLA/SLAFilter.vue';
import WootDatePicker from 'dashboard/components/ui/DatePicker/DatePicker.vue';
import DateRangePicker from 'dashboard/components-next/DatePicker/DateRangePicker.vue';
import { subDays, fromUnixTime } from 'date-fns';
import { getUnixStartOfDay, getUnixEndOfDay } from 'helpers/DateHelper';
import {
@@ -88,7 +88,7 @@ onMounted(() => {
<template>
<div class="flex flex-col flex-wrap w-full gap-3 md:flex-row">
<WootDatePicker
<DateRangePicker
v-model:date-range="customDateRange"
v-model:range-type="selectedDateRange"
@date-range-changed="onDateRangeChange"