fix: attendance clock-in button stuck when geolocation fails or hangs

handlePunch set submitting=true before calling geolocation, but the
error callback never reset it. When geolocation was denied or timed out:
- Error alert showed
- GPS confirm modal opened
- Button stayed disabled showing "Zpracovávám..."
- User thought it was stuck; no server request appeared to happen

Also added a 12s safety timeout fallback because some browsers silently
hang on getCurrentPosition without calling either callback.

Fix: call setSubmitting(false) in the error callback and clear the
safety timeout in both success and error paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-04-24 11:40:51 +02:00
parent 9e699c4dd4
commit c4f6723042

View File

@@ -186,7 +186,18 @@ export default function Attendance() {
setSubmitting(true);
latestActionRef.current = action;
// Safety timeout: if geolocation hangs silently (some browsers ignore
// the timeout option), reset the button so the user isn't stuck.
const safetyTimeout = setTimeout(() => {
if (mountedRef.current) {
setSubmitting(false);
alert.error("Vypršel časový limit pro získání polohy");
setGpsConfirm({ isOpen: true, action });
}
}, 12000);
if (!navigator.geolocation) {
clearTimeout(safetyTimeout);
alert.warning("GPS není dostupná");
submitPunch(action, {});
return;
@@ -194,6 +205,7 @@ export default function Attendance() {
navigator.geolocation.getCurrentPosition(
(position) => {
clearTimeout(safetyTimeout);
if (!mountedRef.current) return;
const { latitude, longitude, accuracy } = position.coords;
submitPunch(action, { latitude, longitude, accuracy, address: "" });
@@ -228,6 +240,7 @@ export default function Attendance() {
.catch(() => {});
},
(geoError) => {
clearTimeout(safetyTimeout);
if (!mountedRef.current) return;
let errorMsg = "Nepodařilo se získat polohu";
if (geoError.code === geoError.PERMISSION_DENIED) {
@@ -236,6 +249,7 @@ export default function Attendance() {
errorMsg = "Vypršel časový limit";
}
alert.error(errorMsg);
setSubmitting(false);
setGpsConfirm({ isOpen: true, action });
},
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 },