Files
pleno-vue/src/composables/useWashProgress.js
T

245 lines
6.0 KiB
JavaScript

import { computed, onUnmounted, ref } from "vue";
const pad2 = (value) => value.toString().padStart(2, "0");
export function useWashProgress(options) {
const {
storageKey = "mywash_progress_v6",
expirationMs = 2 * 60 * 60 * 1000,
currentStep,
washInProgress,
washLaneId,
washStartTime,
licensePlateInput,
vehicleTypeSelect,
radioWashType,
radioLaneOption,
customerNumberInput,
answers,
completedTasks,
nearestDepartmentId,
forceNearestDepartmentEvaluationId,
isForcingNearestDepartment,
applyRestoredState,
} = options;
const isRestoring = ref(true);
const isDestroying = ref(false);
const isDestroyingManual = ref(false);
const completedDurationMs = ref(null);
const now = ref(Date.now());
let timerInterval = null;
let isDestroyingGlobal = false;
let beforeUnloadHandler = null;
const timeSinceWashStart = computed(() => {
if (washInProgress.value && washStartTime.value) {
return now.value - washStartTime.value;
}
return 0;
});
const formattedElapsed = computed(() => {
const totalSeconds = Math.floor(timeSinceWashStart.value / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
if (minutes < 0 || seconds < 0) {
return "00:00";
}
return `${pad2(minutes)}:${pad2(seconds)}`;
});
const loadProgress = () => {
try {
const raw = localStorage.getItem(storageKey);
if (!raw) {
return null;
}
return JSON.parse(raw);
} catch (error) {
console.warn("Failed to load wash progress:", error);
return null;
}
};
const clearProgress = () => {
try {
localStorage.removeItem(storageKey);
} catch {}
};
const saveProgress = (trigger = "manual") => {
if (isRestoring.value) {
return;
}
if (isDestroying.value || isDestroyingManual.value || isDestroyingGlobal) {
if (trigger !== "beforeunload") {
return;
}
}
if (
washInProgress.value
&& currentStep.value === 0
&& trigger !== "onStartWash"
&& trigger !== "onCloseCompleted"
) {
return;
}
if (!washInProgress.value && Number(currentStep.value) === 5) {
clearProgress();
return;
}
try {
const payload = {
washInProgress: washInProgress.value,
washLaneId: washLaneId.value,
washStartTime: washStartTime.value,
currentStep: currentStep.value,
licensePlateInput: licensePlateInput.value,
vehicleTypeSelect: vehicleTypeSelect.value,
radioWashType: radioWashType.value,
radioLaneOption: radioLaneOption.value,
customerNumberInput: customerNumberInput.value,
answers: answers.value,
completedTasks: completedTasks.value,
nearestDepartmentId: nearestDepartmentId(),
forceNearestDepartmentEvaluationId: forceNearestDepartmentEvaluationId.value,
savedAt: Date.now(),
isForcingNearestDepartment: isForcingNearestDepartment.value,
};
localStorage.setItem(storageKey, JSON.stringify(payload));
} catch (error) {
console.warn("Failed to save wash progress:", error);
}
};
const startElapsedTimer = () => {
if (timerInterval) {
clearInterval(timerInterval);
}
timerInterval = setInterval(() => {
now.value = Date.now();
if (washInProgress.value) {
saveProgress("timer");
}
}, 1000);
};
const stopElapsedTimer = () => {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
}
};
const markDestroying = () => {
isDestroyingGlobal = true;
isDestroyingManual.value = true;
isDestroying.value = true;
};
const restoreProgress = () => {
const savedProgress = loadProgress();
if (!savedProgress) {
isRestoring.value = false;
return null;
}
const timeDiff = Date.now() - (savedProgress.savedAt || 0);
if (timeDiff >= expirationMs) {
clearProgress();
isRestoring.value = false;
return null;
}
applyRestoredState(savedProgress);
if (washInProgress.value) {
startElapsedTimer();
}
const restoredStep = savedProgress.currentStep;
const isSameRestoredProgress = () => (
Boolean(washInProgress.value) === Boolean(savedProgress.washInProgress) &&
String(washLaneId.value ?? "") === String(savedProgress.washLaneId ?? "") &&
String(washStartTime.value ?? "") === String(savedProgress.washStartTime ?? "") &&
String(licensePlateInput.value ?? "") === String(savedProgress.licensePlateInput ?? "") &&
String(vehicleTypeSelect.value ?? "") === String(savedProgress.vehicleTypeSelect ?? "") &&
String(radioLaneOption.value ?? "") === String(savedProgress.radioLaneOption ?? "")
);
setTimeout(() => {
if (isSameRestoredProgress() && currentStep.value !== restoredStep) {
currentStep.value = restoredStep;
}
isRestoring.value = false;
}, 1500);
return savedProgress;
};
const registerBeforeUnload = () => {
if (typeof window === "undefined") {
return () => {};
}
beforeUnloadHandler = () => {
markDestroying();
saveProgress("beforeunload");
};
window.addEventListener("beforeunload", beforeUnloadHandler);
return () => {
if (beforeUnloadHandler) {
window.removeEventListener("beforeunload", beforeUnloadHandler);
beforeUnloadHandler = null;
}
};
};
const cleanup = () => {
markDestroying();
stopElapsedTimer();
if (typeof window !== "undefined" && beforeUnloadHandler) {
window.removeEventListener("beforeunload", beforeUnloadHandler);
beforeUnloadHandler = null;
}
};
onUnmounted(() => {
cleanup();
});
return {
completedDurationMs,
now,
isRestoring,
isDestroying,
isDestroyingManual,
timeSinceWashStart,
formattedElapsed,
loadProgress,
saveProgress,
clearProgress,
restoreProgress,
startElapsedTimer,
stopElapsedTimer,
markDestroying,
registerBeforeUnload,
cleanup,
};
}