|
| 1 | +<!-- |
| 2 | + Copyright (C) 2026 Linagora |
| 3 | +
|
| 4 | + This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General |
| 5 | + Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) |
| 6 | + any later version, provided you comply with the Additional Terms applicable for LinID Identity Manager software by |
| 7 | + LINAGORA pursuant to Section 7 of the GNU Affero General Public License, subsections (b), (c), and (e), pursuant to |
| 8 | + which these Appropriate Legal Notices must notably (i) retain the display of the "LinID™" trademark/logo at the top |
| 9 | + of the interface window, the display of the “You are using the Open Source and free version of LinID™, powered by |
| 10 | + Linagora © 2009–2013. Contribute to LinID R&D by subscribing to an Enterprise offer!” infobox and in the e-mails |
| 11 | + sent with the Program, notice appended to any type of outbound messages (e.g. e-mail and meeting requests) as well |
| 12 | + as in the LinID Identity Manager user interface, (ii) retain all hypertext links between LinID Identity Manager |
| 13 | + and https://linid.org/, as well as between LINAGORA and LINAGORA.com, and (iii) refrain from infringing LINAGORA |
| 14 | + intellectual property rights over its trademarks and commercial brands. Other Additional Terms apply, see |
| 15 | + <http://www.linagora.com/licenses/> for more details. |
| 16 | +
|
| 17 | + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| 18 | + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
| 19 | + details. |
| 20 | +
|
| 21 | + You should have received a copy of the GNU Affero General Public License and its applicable Additional Terms for |
| 22 | + LinID Identity Manager along with this program. If not, see <http://www.gnu.org/licenses/> for the GNU Affero |
| 23 | + General Public License version 3 and <http://www.linagora.com/licenses/> for the Additional Terms applicable to the |
| 24 | + LinID Identity Manager software. |
| 25 | +--> |
| 26 | + |
| 27 | +<template> |
| 28 | + <!-- v8 ignore start --> |
| 29 | + <div |
| 30 | + class="date-search-filter-panel" |
| 31 | + data-cy="date-search-filter-panel" |
| 32 | + > |
| 33 | + <q-input |
| 34 | + v-model="inputValue" |
| 35 | + v-bind="uiProps.input" |
| 36 | + type="text" |
| 37 | + :label="translateOrDefault('', 'inputLabel')" |
| 38 | + :hint="translateOrDefault('', 'inputHint')" |
| 39 | + :prefix="translateOrDefault('', 'inputPrefix')" |
| 40 | + :suffix="translateOrDefault('', 'inputSuffix')" |
| 41 | + :rules="rules" |
| 42 | + data-cy="date-search-filter-panel_input" |
| 43 | + > |
| 44 | + <template #append> |
| 45 | + <q-icon |
| 46 | + name="event" |
| 47 | + class="cursor-pointer" |
| 48 | + v-bind="uiProps.icon" |
| 49 | + > |
| 50 | + <q-popup-proxy |
| 51 | + cover |
| 52 | + transition-show="scale" |
| 53 | + transition-hide="scale" |
| 54 | + > |
| 55 | + <q-date |
| 56 | + v-model="inputValue" |
| 57 | + :mask="computedMask" |
| 58 | + v-bind="uiProps.date" |
| 59 | + data-cy="date-search-filter-panel_datepicker" |
| 60 | + > |
| 61 | + <div class="row items-center justify-end"> |
| 62 | + <q-btn |
| 63 | + v-close-popup |
| 64 | + :label="t('close')" |
| 65 | + v-bind="uiProps.closeButton" |
| 66 | + /> |
| 67 | + </div> |
| 68 | + </q-date> |
| 69 | + </q-popup-proxy> |
| 70 | + </q-icon> |
| 71 | + </template> |
| 72 | + </q-input> |
| 73 | + |
| 74 | + <q-checkbox |
| 75 | + v-model="isNegation" |
| 76 | + v-bind="uiProps.checkbox" |
| 77 | + :label="t('negateLabel')" |
| 78 | + data-cy="date-search-filter-panel_negate" |
| 79 | + /> |
| 80 | + |
| 81 | + <q-option-group |
| 82 | + v-model="selectedOperatorKey" |
| 83 | + v-bind="uiProps.optionGroup" |
| 84 | + :options="operators" |
| 85 | + type="radio" |
| 86 | + data-cy="date-search-filter-panel_operators" |
| 87 | + /> |
| 88 | + |
| 89 | + <q-btn |
| 90 | + v-bind="uiProps.searchButton" |
| 91 | + :label="t('searchButton')" |
| 92 | + data-cy="date-search-filter-panel_search" |
| 93 | + @click="onSearch" |
| 94 | + /> |
| 95 | + </div> |
| 96 | + <!-- v8 ignore stop --> |
| 97 | +</template> |
| 98 | + |
| 99 | +<script setup lang="ts"> |
| 100 | +import type { |
| 101 | + LinidFilterOperator, |
| 102 | + LinidQBtnProps, |
| 103 | + LinidQCheckboxProps, |
| 104 | + LinidQDateProps, |
| 105 | + LinidQIconProps, |
| 106 | + LinidQInputProps, |
| 107 | + LinidQOptionGroupProps, |
| 108 | +} from '@linagora/linid-im-front-corelib'; |
| 109 | +import { |
| 110 | + getI18nInstance, |
| 111 | + LinidFilterValue, |
| 112 | + QDATE_DEFAULT_MASK, |
| 113 | + useQuasarFieldValidation, |
| 114 | + useScopedI18n, |
| 115 | + useUiDesign, |
| 116 | +} from '@linagora/linid-im-front-corelib'; |
| 117 | +import type { ValidationRule } from 'quasar'; |
| 118 | +import { computed, ref } from 'vue'; |
| 119 | +import type { |
| 120 | + DateFilterOperatorKey, |
| 121 | + DateSearchFilterPanelProps, |
| 122 | + DateSearchFilterPanelUIProps, |
| 123 | +} from '../../types/dateSearchFilterPanel'; |
| 124 | +import type { LinidFilterPanelSearchOutputs } from '../../types/linidFilterPanel'; |
| 125 | +
|
| 126 | +const OPERATOR_KEYS: DateFilterOperatorKey[] = [ |
| 127 | + 'inferior', |
| 128 | + 'superior', |
| 129 | + 'equals', |
| 130 | +]; |
| 131 | +
|
| 132 | +const OPERATOR_MAP: Record<DateFilterOperatorKey, LinidFilterOperator> = { |
| 133 | + inferior: 'lt_', |
| 134 | + superior: 'gt_', |
| 135 | + equals: '', |
| 136 | +}; |
| 137 | +
|
| 138 | +const props = defineProps<DateSearchFilterPanelProps>(); |
| 139 | +const emit = defineEmits<LinidFilterPanelSearchOutputs>(); |
| 140 | +
|
| 141 | +const { t, translateOrDefault } = useScopedI18n( |
| 142 | + `${props.i18nScope}.DateSearchFilterPanel` |
| 143 | +); |
| 144 | +const { ui } = useUiDesign(); |
| 145 | +
|
| 146 | +const localUiNamespace = `${props.uiNamespace}.date-search-filter-panel`; |
| 147 | +
|
| 148 | +const uiProps: DateSearchFilterPanelUIProps = { |
| 149 | + input: ui<LinidQInputProps>(localUiNamespace, 'q-input'), |
| 150 | + icon: ui<LinidQIconProps>(localUiNamespace, 'q-icon'), |
| 151 | + date: ui<LinidQDateProps>(localUiNamespace, 'q-date'), |
| 152 | + closeButton: ui<LinidQBtnProps>(`${localUiNamespace}.close`, 'q-btn'), |
| 153 | + checkbox: ui<LinidQCheckboxProps>(localUiNamespace, 'q-checkbox'), |
| 154 | + optionGroup: ui<LinidQOptionGroupProps>(localUiNamespace, 'q-option-group'), |
| 155 | + searchButton: ui<LinidQBtnProps>(localUiNamespace, 'q-btn'), |
| 156 | +}; |
| 157 | +
|
| 158 | +const inputValue = ref(''); |
| 159 | +const isNegation = ref(false); |
| 160 | +const selectedOperatorKey = ref<DateFilterOperatorKey>('equals'); |
| 161 | +
|
| 162 | +const computedMask = computed(() => { |
| 163 | + const { te, t } = getI18nInstance().global; |
| 164 | + if (props.maskI18NKey && te(props.maskI18NKey)) { |
| 165 | + return t(props.maskI18NKey); |
| 166 | + } |
| 167 | + return props.mask ?? QDATE_DEFAULT_MASK; |
| 168 | +}); |
| 169 | +
|
| 170 | +const { validDate } = useQuasarFieldValidation( |
| 171 | + `${props.i18nScope}.DateSearchFilterPanel` |
| 172 | +); |
| 173 | +
|
| 174 | +const rules = computed<ValidationRule[]>(() => [validDate(computedMask.value)]); |
| 175 | +
|
| 176 | +const operators = computed(() => |
| 177 | + OPERATOR_KEYS.map((key) => ({ |
| 178 | + value: key, |
| 179 | + label: isNegation.value |
| 180 | + ? t(`operators.not${key.charAt(0).toUpperCase()}${key.slice(1)}`) |
| 181 | + : t(`operators.${key}`), |
| 182 | + })) |
| 183 | +); |
| 184 | +
|
| 185 | +/** |
| 186 | + * Formats the raw input value into the date string expected by the backend for the selected operator. |
| 187 | + * @returns The formatted date string. |
| 188 | + */ |
| 189 | +function buildDateValue(): string { |
| 190 | + const v = inputValue.value; |
| 191 | + switch (selectedOperatorKey.value) { |
| 192 | + case 'equals': |
| 193 | + return `${v} 00:00:00_bt_${v} 23:59:59`; |
| 194 | + case 'inferior': |
| 195 | + return `${v} 00:00:00`; |
| 196 | + case 'superior': |
| 197 | + return `${v} 23:59:59`; |
| 198 | + } |
| 199 | +} |
| 200 | +
|
| 201 | +/** |
| 202 | + * Builds the LinidFilterValue for the current form state. |
| 203 | + * @returns The constructed filter value. |
| 204 | + */ |
| 205 | +function buildFilterValue(): LinidFilterValue { |
| 206 | + return new LinidFilterValue( |
| 207 | + isNegation.value, |
| 208 | + OPERATOR_MAP[selectedOperatorKey.value], |
| 209 | + buildDateValue() |
| 210 | + ); |
| 211 | +} |
| 212 | +
|
| 213 | +/** |
| 214 | + * Emits the search event with the constructed filter value. |
| 215 | + */ |
| 216 | +function onSearch(): void { |
| 217 | + emit('search', { |
| 218 | + field: props.fieldName, |
| 219 | + values: [buildFilterValue()], |
| 220 | + }); |
| 221 | +} |
| 222 | +</script> |
0 commit comments