- corretto componente CDateTime

- aggiunto componente CDateTimeStartEnd
This commit is contained in:
Surya Paolo
2025-10-05 23:58:57 +02:00
parent 58db59a751
commit 2f39d4f1d9
71 changed files with 1559 additions and 1642 deletions

View File

@@ -0,0 +1,363 @@
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, null] as unknown as () => string | null, default: null },
endValue: { type: [String, 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 allinizio mantenendo lora 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 linizio';
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,
};
},
});