245 lines
6.0 KiB
JavaScript
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,
|
|
};
|
|
}
|