Il problema era sulla fetch nel service worker, gestita in quel modo personalizzato, andava in conflitto, non tenendo le chiamate bloccanti, ma uscivano prima che arrivasse la risposta del server. - Per chi è da tanto che non si collega a RISO, compare "Email non verificata"... (si risolve chiudendo su ESCI e riloggandosi)... però andrebbe sistemata. (stesso problema di prima).
364 lines
11 KiB
TypeScript
Executable File
364 lines
11 KiB
TypeScript
Executable File
import { defineComponent, ref, watch, computed } from 'vue';
|
||
import { useQuasar } from 'quasar';
|
||
import { useI18n } from 'vue-i18n';
|
||
import { tools } from '@src/store/Modules/tools';
|
||
import { toolsext } from '@store/Modules/toolsext';
|
||
|
||
function toTS(s: string | null): number | null {
|
||
if (!s) return null;
|
||
const iso = s.includes('T') ? s : s.replace(' ', 'T');
|
||
const ts = Date.parse(iso);
|
||
return Number.isNaN(ts) ? null : ts;
|
||
}
|
||
|
||
export default defineComponent({
|
||
name: 'CDateTimeStartEnd',
|
||
emits: [
|
||
'update:startValue',
|
||
'update:endValue',
|
||
'show',
|
||
'savetoclose',
|
||
'clear-start',
|
||
'clear-end',
|
||
],
|
||
props: {
|
||
startValue: { type: [String, Date, null] as unknown as () => string | null, default: null },
|
||
endValue: { type: [String, Date, null] as unknown as () => string | null, default: null },
|
||
|
||
startLabel: { type: String, default: 'Inizio' },
|
||
endLabel: { type: String, default: 'Fine' },
|
||
|
||
data_class: { type: String, default: '' },
|
||
canEdit: { type: Boolean, default: true },
|
||
disable: { type: Boolean, default: false },
|
||
bgcolor: { type: String, default: '' },
|
||
dense: { type: Boolean, default: false },
|
||
|
||
view: { type: String as () => 'date-time' | 'date' | 'time', default: 'date-time' },
|
||
nullableStart: { type: Boolean, default: true },
|
||
nullableEnd: { type: Boolean, default: true },
|
||
nullText: { type: String, default: '—' },
|
||
|
||
calendarIcon: { type: String, default: 'fas fa-calendar-day' },
|
||
clockIcon: { type: String, default: 'fas fa-clock' },
|
||
clearIcon: { type: String, default: 'fas fa-ban' },
|
||
|
||
optionalText: { type: String, default: 'opzionale' },
|
||
enableEndText: { type: String, default: 'Attiva' },
|
||
},
|
||
setup(props, { emit }) {
|
||
const $q = useQuasar();
|
||
const { t } = useI18n();
|
||
|
||
const isMobile = computed(() => $q.screen.lt.sm);
|
||
|
||
// local state
|
||
const startVal = ref<string | null>(null);
|
||
const endVal = ref<string | null>(null);
|
||
const startPrev = ref<string | null>(null);
|
||
const endPrev = ref<string | null>(null);
|
||
|
||
// dialog states
|
||
const startDateDialog = ref(false);
|
||
const startTimeDialog = ref(false);
|
||
const endDateDialog = ref(false);
|
||
const endTimeDialog = ref(false);
|
||
|
||
const startDateDesktopDialog = ref(false);
|
||
const startTimeDesktopDialog = ref(false);
|
||
const endDateDesktopDialog = ref(false);
|
||
const endTimeDesktopDialog = ref(false);
|
||
|
||
const endError = ref<string>('');
|
||
|
||
// sync props -> local
|
||
watch(
|
||
() => props.startValue,
|
||
(v) => {
|
||
startVal.value = v ? tools.getstrYYMMDDDateTime(v) : null;
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
watch(
|
||
() => props.endValue,
|
||
(v) => {
|
||
endVal.value = v ? tools.getstrYYMMDDDateTime(v) : null;
|
||
validateRange();
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
function getstrDate(val: string | Date | null) {
|
||
if (!val) return props.nullText;
|
||
if (props.view === 'date-time') return tools.getstrDateTime(val);
|
||
if (props.view === 'date') return tools.getstrDate(val);
|
||
return tools.getstrTime(val);
|
||
}
|
||
|
||
function opening(kind: 'start' | 'end') {
|
||
if (kind === 'start') {
|
||
startPrev.value = startVal.value;
|
||
if (!startVal.value) startVal.value = tools.getstrYYMMDDDateTime(new Date());
|
||
} else {
|
||
endPrev.value = endVal.value;
|
||
if (!endVal.value)
|
||
endVal.value = startVal.value || tools.getstrYYMMDDDateTime(new Date());
|
||
}
|
||
endError.value = '';
|
||
emit('show');
|
||
}
|
||
|
||
function cancelStart() {
|
||
startVal.value = startPrev.value;
|
||
closeAllStart();
|
||
}
|
||
function cancelEnd() {
|
||
endVal.value = endPrev.value;
|
||
endError.value = '';
|
||
closeAllEnd();
|
||
}
|
||
|
||
function saveStart() {
|
||
const currStart = startVal.value;
|
||
const prevStart = startPrev.value;
|
||
|
||
// salva start
|
||
emit('update:startValue', currStart as unknown as string);
|
||
emit('savetoclose', { which: 'start', current: currStart, prev: prevStart });
|
||
|
||
// Se esiste end e l'intervallo è invalido, riallinea fine preservando l'ora originale
|
||
if (endVal.value) {
|
||
const tsStart = toTS(currStart);
|
||
const tsEnd = toTS(endVal.value);
|
||
if (tsStart !== null && tsEnd !== null && tsEnd < tsStart) {
|
||
endVal.value = buildEndWithStartDatePreservingEndTime(tsStart, tsEnd);
|
||
endError.value = '';
|
||
emit('update:endValue', endVal.value as unknown as string);
|
||
emit('savetoclose', { which: 'end', current: endVal.value, prev: endPrev.value });
|
||
tools.showNeutralNotif(
|
||
$q,
|
||
t('date.rangeFixed') || 'Fine allineata all’inizio mantenendo l’ora originale'
|
||
);
|
||
}
|
||
}
|
||
|
||
closeAllStart();
|
||
}
|
||
|
||
/**
|
||
* Costruisce una nuova data di fine usando:
|
||
* - data (Y/M/D) = quella di start
|
||
* - ora (h:m:s:ms) = quella di end originale
|
||
* Se il risultato è ancora < start, sposta end al giorno successivo mantenendo la stessa ora.
|
||
*/
|
||
function buildEndWithStartDatePreservingEndTime(
|
||
tsStart: number,
|
||
tsEnd: number
|
||
): string {
|
||
// Se il componente è solo 'date', non c'è ora da preservare: end = start
|
||
if (props.view === 'date') {
|
||
return tools.getstrYYMMDDDateTime(new Date(tsStart));
|
||
}
|
||
|
||
const dStart = new Date(tsStart);
|
||
const dEnd = new Date(tsEnd);
|
||
|
||
// Ricostruisci end: data = start, ora = end originale
|
||
const newEnd = new Date(
|
||
dStart.getFullYear(),
|
||
dStart.getMonth(),
|
||
dStart.getDate(),
|
||
dEnd.getHours(),
|
||
dEnd.getMinutes(),
|
||
dEnd.getSeconds(),
|
||
dEnd.getMilliseconds()
|
||
);
|
||
|
||
// Se così è ancora < start (es. end 08:00, start 10:00 stesso giorno) → bump di 1 giorno
|
||
if (newEnd.getTime() < tsStart) {
|
||
newEnd.setDate(newEnd.getDate() + 1);
|
||
}
|
||
|
||
return tools.getstrYYMMDDDateTime(newEnd);
|
||
}
|
||
|
||
function saveEnd() {
|
||
if (!validateRange(true)) {
|
||
// blocco il salvataggio se ancora invalida
|
||
return;
|
||
}
|
||
emit('update:endValue', endVal.value as unknown as string);
|
||
emit('savetoclose', { which: 'end', current: endVal.value, prev: endPrev.value });
|
||
closeAllEnd();
|
||
}
|
||
|
||
function onStartChange(v: string | Date) {
|
||
startVal.value = typeof v === 'string' ? v : tools.getstrYYMMDDDateTime(v);
|
||
}
|
||
function onEndChange(v: string | Date) {
|
||
endVal.value = typeof v === 'string' ? v : tools.getstrYYMMDDDateTime(v);
|
||
validateRange();
|
||
}
|
||
|
||
function clearStart() {
|
||
startVal.value = null;
|
||
emit('update:startValue', null as unknown as string);
|
||
emit('savetoclose', { which: 'start', current: null, prev: startPrev.value });
|
||
// se non c'è inizio, rimuovo anche fine per coerenza
|
||
if (endVal.value) {
|
||
endVal.value = null;
|
||
emit('update:endValue', null as unknown as string);
|
||
emit('savetoclose', { which: 'end', current: null, prev: endPrev.value });
|
||
}
|
||
emit('clear-start');
|
||
tools.showNeutralNotif($q, t('common.cleared') || 'Valore rimosso');
|
||
closeAllStart();
|
||
}
|
||
function clearEnd() {
|
||
endVal.value = null;
|
||
emit('update:endValue', null as unknown as string);
|
||
emit('clear-end');
|
||
tools.showNeutralNotif($q, t('common.cleared') || 'Valore rimosso');
|
||
endError.value = '';
|
||
closeAllEnd();
|
||
}
|
||
function enableEnd() {
|
||
endVal.value = startVal.value || tools.getstrYYMMDDDateTime(new Date());
|
||
validateRange(true);
|
||
emit('update:endValue', endVal.value as unknown as string);
|
||
}
|
||
|
||
function openStartDate() {
|
||
opening('start');
|
||
if (isMobile.value) startDateDialog.value = true;
|
||
else startDateDesktopDialog.value = true;
|
||
}
|
||
function openStartTime() {
|
||
opening('start');
|
||
if (isMobile.value) startTimeDialog.value = true;
|
||
else startTimeDesktopDialog.value = true;
|
||
}
|
||
function openEndDate() {
|
||
opening('end');
|
||
if (isMobile.value) endDateDialog.value = true;
|
||
else endDateDesktopDialog.value = true;
|
||
}
|
||
function openEndTime() {
|
||
opening('end');
|
||
if (isMobile.value) endTimeDialog.value = true;
|
||
else endTimeDesktopDialog.value = true;
|
||
}
|
||
|
||
function closeAllStart() {
|
||
startDateDialog.value = false;
|
||
startTimeDialog.value = false;
|
||
startDateDesktopDialog.value = false;
|
||
startTimeDesktopDialog.value = false;
|
||
}
|
||
function closeAllEnd() {
|
||
endDateDialog.value = false;
|
||
endTimeDialog.value = false;
|
||
endDateDesktopDialog.value = false;
|
||
endTimeDesktopDialog.value = false;
|
||
}
|
||
|
||
function validateRange(showMsg = false): boolean {
|
||
endError.value = '';
|
||
const tsStart = toTS(startVal.value);
|
||
const tsEnd = toTS(endVal.value);
|
||
if (tsStart != null && tsEnd != null && tsEnd < tsStart) {
|
||
endError.value =
|
||
t('date.invalidRange') || 'La data di fine non può precedere l’inizio';
|
||
if (showMsg) tools.showNeutralNotif($q, endError.value);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
const confirmLabelStartDate = computed(() =>
|
||
isMobile.value
|
||
? t('common.set') || 'Imposta'
|
||
: `Imposta a ${tools.getstrDateLong(startVal.value)}`
|
||
);
|
||
const confirmLabelStartTime = computed(() =>
|
||
isMobile.value
|
||
? t('common.set') || 'Imposta'
|
||
: `Imposta a ${tools.getstrTime(startVal.value)}`
|
||
);
|
||
const confirmLabelEndDate = computed(() =>
|
||
isMobile.value
|
||
? t('common.set') || 'Imposta'
|
||
: `Imposta a ${tools.getstrDateLong(endVal.value)}`
|
||
);
|
||
const confirmLabelEndTime = computed(() =>
|
||
isMobile.value
|
||
? t('common.set') || 'Imposta'
|
||
: `Imposta a ${tools.getstrTime(endVal.value)}`
|
||
);
|
||
|
||
const canEditEnd = computed(() => !!endVal.value || !props.nullableEnd);
|
||
|
||
return {
|
||
toolsext,
|
||
tools,
|
||
// values & labels
|
||
startVal,
|
||
endVal,
|
||
startLabel: computed(() => props.startLabel),
|
||
endLabel: computed(() => props.endLabel),
|
||
|
||
// ui flags
|
||
dense: props.dense,
|
||
bgcolor: props.bgcolor,
|
||
disable: props.disable,
|
||
data_class: props.data_class,
|
||
canEdit: props.canEdit,
|
||
nullableStart: props.nullableStart,
|
||
nullableEnd: props.nullableEnd,
|
||
nullText: props.nullText,
|
||
optionalText: props.optionalText,
|
||
enableEndText: props.enableEndText,
|
||
|
||
calendarIcon: props.calendarIcon,
|
||
clockIcon: props.clockIcon,
|
||
clearIcon: props.clearIcon,
|
||
|
||
// dialogs
|
||
startDateDialog,
|
||
startTimeDialog,
|
||
endDateDialog,
|
||
endTimeDialog,
|
||
startDateDesktopDialog,
|
||
startTimeDesktopDialog,
|
||
endDateDesktopDialog,
|
||
endTimeDesktopDialog,
|
||
|
||
// methods
|
||
getstrDate,
|
||
openStartDate,
|
||
openStartTime,
|
||
openEndDate,
|
||
openEndTime,
|
||
cancelStart,
|
||
cancelEnd,
|
||
saveStart,
|
||
saveEnd,
|
||
onStartChange,
|
||
onEndChange,
|
||
clearStart,
|
||
clearEnd,
|
||
enableEnd,
|
||
|
||
// helpers
|
||
confirmLabelStartDate,
|
||
confirmLabelStartTime,
|
||
confirmLabelEndDate,
|
||
confirmLabelEndTime,
|
||
isMobile,
|
||
canEditEnd,
|
||
|
||
endError,
|
||
};
|
||
},
|
||
});
|