Sep 15 - Reverse the check-in pages, set type of work Regular as default
This commit is contained in:
@@ -547,15 +547,12 @@ Toggle UI in `create_qr_code.html` and `edit_qr_code.html` — uses `addEventLis
|
||||
### Type of Work — Check-In Dropdown (Sept 2026)
|
||||
|
||||
The check-in page carries a **Type of Work / Tipo de Trabajo** `<select>` directly after
|
||||
the Employee ID field. The employee enters a **numeric-only** ID and picks the type.
|
||||
**There is no default (Sept 14, 2026)** — the dropdown opens on a disabled placeholder
|
||||
and submit is blocked (red outline + bilingual message) until a type is chosen, because
|
||||
employees were leaving it on Regular without looking.
|
||||
the Employee ID field. The employee enters a **numeric-only** ID and picks the type;
|
||||
`Regular` is selected by default.
|
||||
|
||||
| Option value | Label shown (bilingual, one plain string) |
|
||||
|---|---|
|
||||
| `""` (disabled, selected) | -- Select type of work / Seleccione tipo de trabajo -- |
|
||||
| `R` | Regular / Trabajo Regular — stored as the plain numeric ID |
|
||||
| `""` | Regular / Trabajo Regular |
|
||||
| `PW` | Periodic Work / Trabajo Periódico (PW) |
|
||||
| `SP` | Special Project / Proyecto Especial (SP) |
|
||||
| `C` | Covering / Cobertura (C) |
|
||||
@@ -567,11 +564,7 @@ languages share one label separated by `/`.
|
||||
the format every calculator and export already parses:
|
||||
|
||||
```python
|
||||
work_type = request.form.get('work_type', '').strip().upper()
|
||||
if not work_type:
|
||||
return jsonify({...}), 400 # nothing chosen — rejected
|
||||
if work_type == 'R':
|
||||
work_type = '' # Regular — no code appended
|
||||
work_type = request.form.get('work_type', '').strip().upper() # '' = Regular
|
||||
if work_type and work_type not in VALID_CHECKIN_WORK_TYPES: # ('SP','PW','PT','C')
|
||||
return jsonify({...}), 400 # reject unknown codes
|
||||
if employee_id and work_type:
|
||||
@@ -677,7 +670,7 @@ load (i.e. after the fetch resolves) and caches plain GETs aggressively:
|
||||
|
||||
### Check-In Flow
|
||||
1. Employee scans QR → `qr_destination.html`
|
||||
2. Enters numeric ID and **must** pick Type of Work (no default); GPS captured by browser
|
||||
2. Enters numeric ID and picks Type of Work (Regular by default); GPS captured by browser
|
||||
3. On a Check Out scan the page pre-selects the open check-in's work type
|
||||
4. Work-type code appended to the ID server-side (`1234` → `1234SP`)
|
||||
5. 30-min interval guard (configurable via `TIME_INTERVAL`)
|
||||
@@ -1197,13 +1190,6 @@ exact-match test is what dropped every SP/PW/PT row the query had already return
|
||||
| `templates/time_attendance_records.html` | Tooltip on the Export by Building button |
|
||||
| — | Replaces the manual "Copilot" procedure (delete PM rows + SP rows, then build a weekly table). Decisions: PM IDs removed from both sheets; SP hours excluded from weekly hours |
|
||||
|
||||
### Set 19 — Type of Work Must Be Selected (Sept 14, 2026)
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `templates/qr_destination.html` | Disabled placeholder option is the default; Regular value `""` → `R`; `required`; submit guard + `.work-type-missing` red outline; check-out suggestion maps server `""` → `R` |
|
||||
| `static/js/qr_destination.js` | Same guard in `submitCheckin()` |
|
||||
| `routes/qr_codes.py` | `qr_checkin` rejects an empty `work_type` with HTTP 400; `R` normalised to `''` (Regular). `last-work-type` still returns `""` for Regular |
|
||||
|
||||
---
|
||||
|
||||
## 21. Infrastructure & Deployment
|
||||
|
||||
+1
-12
@@ -743,19 +743,8 @@ def qr_checkin(qr_url):
|
||||
# The employee enters a numeric ID and picks a work type; the code is
|
||||
# appended to the ID so the stored value keeps the existing storage
|
||||
# format ("1234SP") that every calculator and export already parses.
|
||||
# The dropdown has no default: 'R' = Regular (ID stored unchanged),
|
||||
# and an empty value means the employee did not choose — rejected.
|
||||
# Empty selection = Regular work — the ID is stored unchanged.
|
||||
work_type = request.form.get('work_type', '').strip().upper()
|
||||
if not work_type:
|
||||
logger_handler.logger.warning(
|
||||
f"Check-in rejected: no type of work selected for employee {employee_id}"
|
||||
)
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Please select the Type of Work. / Por favor seleccione el Tipo de Trabajo.'
|
||||
}), 400
|
||||
if work_type == 'R':
|
||||
work_type = '' # Regular — no code appended
|
||||
if work_type and work_type not in VALID_CHECKIN_WORK_TYPES:
|
||||
logger_handler.logger.warning(
|
||||
f"Check-in rejected: invalid work type '{work_type}' for employee {employee_id}"
|
||||
|
||||
@@ -327,25 +327,10 @@ function submitCheckin() {
|
||||
const workTypeField = document.getElementById("work_type");
|
||||
const workType = workTypeField ? workTypeField.value.trim() : "";
|
||||
|
||||
// Type of Work has no default - the employee must choose one
|
||||
if (!workType) {
|
||||
if (workTypeField) {
|
||||
workTypeField.classList.add("work-type-missing");
|
||||
workTypeField.focus();
|
||||
}
|
||||
showCustomStatusMessage(
|
||||
"Please select the Type of Work / Por favor seleccione el Tipo de Trabajo",
|
||||
"error"
|
||||
);
|
||||
isSubmitting = false;
|
||||
updateSubmitButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare form data
|
||||
const formData = new FormData();
|
||||
formData.append("employee_id", employeeId);
|
||||
// R = Regular (ID stored unchanged); PW / SP / C are appended to the ID server-side
|
||||
// Empty string = Regular; PW / SP / C are appended to the ID server-side
|
||||
formData.append("work_type", workType);
|
||||
formData.append(
|
||||
"latitude",
|
||||
|
||||
@@ -69,12 +69,6 @@
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* Type of Work has no default - flagged when submit is tried without one */
|
||||
select.work-type-missing {
|
||||
border: 2px solid #ef4444 !important;
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2) !important;
|
||||
}
|
||||
|
||||
.bilingual-container {
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-radius: 8px;
|
||||
@@ -1224,16 +1218,11 @@
|
||||
name="work_type"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
required
|
||||
>
|
||||
<!-- <option> cannot hold the coloured english-text/spanish-text
|
||||
spans used elsewhere, so both languages share one plain label,
|
||||
separated by "/" like the Employee ID placeholder above.
|
||||
No default: the employee must pick a type before submitting.
|
||||
Regular is "R" so it is distinguishable from "nothing picked";
|
||||
the server stores it as the plain numeric ID. -->
|
||||
<option value="" selected disabled>-- Select type of work / Seleccione tipo de trabajo --</option>
|
||||
<option value="R">Regular / Trabajo Regular</option>
|
||||
separated by "/" like the Employee ID placeholder above. -->
|
||||
<option value="" selected>Regular / Trabajo Regular</option>
|
||||
<option value="PW">Periodic Work / Trabajo Periódico (PW)</option>
|
||||
<option value="SP">Special Project / Proyecto Especial (SP)</option>
|
||||
<option value="C">Covering / Cobertura (C)</option>
|
||||
@@ -1446,8 +1435,7 @@
|
||||
// Bilingual labels - mirror WORK_TYPE_LABELS in routes/qr_codes.py.
|
||||
const WORK_TYPE_LABELS = {
|
||||
"": { en: "Regular", es: "Trabajo Regular" },
|
||||
R: { en: "Regular", es: "Trabajo Regular" },
|
||||
PW: { en: "Periodic Work", es: "Trabajo Periódico" },
|
||||
PW: { en: "Periodic Work", es: "Trabajo Periódico" },
|
||||
SP: { en: "Special Project", es: "Proyecto Especial" },
|
||||
PT: { en: "Project Team", es: "Equipo de Proyecto" },
|
||||
C: { en: "Covering", es: "Cobertura" },
|
||||
@@ -1514,14 +1502,8 @@
|
||||
return;
|
||||
}
|
||||
const field = document.getElementById("work_type");
|
||||
// The server reports an open Regular check-in as ""; the dropdown's
|
||||
// Regular option is "R" ("" is the "nothing selected" placeholder).
|
||||
const optionValue = suggestedWorkTypeCode === "" ? "R" : suggestedWorkTypeCode;
|
||||
if (field && field.value !== optionValue) {
|
||||
field.value = optionValue;
|
||||
}
|
||||
if (field && field.value) {
|
||||
field.classList.remove("work-type-missing");
|
||||
if (field && field.value !== suggestedWorkTypeCode) {
|
||||
field.value = suggestedWorkTypeCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1645,9 +1627,6 @@
|
||||
});
|
||||
|
||||
field.addEventListener("change", function () {
|
||||
if (field.value) {
|
||||
field.classList.remove("work-type-missing");
|
||||
}
|
||||
if (!workTypeTouchedByEmployee) {
|
||||
// No interaction preceded this change: it is Safari restoring the
|
||||
// control, not the employee choosing. Re-assert the suggestion.
|
||||
@@ -1935,19 +1914,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Type of Work has no default - the employee must choose one
|
||||
if (!workType) {
|
||||
if (workTypeField) {
|
||||
workTypeField.classList.add("work-type-missing");
|
||||
workTypeField.focus();
|
||||
}
|
||||
showStatusMessage(
|
||||
"Please select the Type of Work / Por favor seleccione el Tipo de Trabajo",
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save Employee ID to localStorage for next time
|
||||
try {
|
||||
localStorage.setItem("qr_last_employee_id", employeeId);
|
||||
@@ -1978,7 +1944,7 @@
|
||||
// Prepare form data with location information
|
||||
const formData = new FormData();
|
||||
formData.append("employee_id", employeeId);
|
||||
// R = Regular (ID stored unchanged); PW / SP / C are appended to the ID server-side
|
||||
// Empty string = Regular; PW / SP / C are appended to the ID server-side
|
||||
formData.append("work_type", workType);
|
||||
|
||||
// Read selected location — window globals (most reliable), then hidden field, then dropdown
|
||||
|
||||
Reference in New Issue
Block a user