feat(time-tracking): wire GPS capture into field clock-in/out (cycle 12 polish)
Deploy to route.crispygoat.com / deploy (push) Has been cancelled
Deploy to route.crispygoat.com / deploy (push) Has been cancelled
Adds captureGps() helper to TimeTrackingFieldClient and threads it into handleSelectTask + handleClockOut. Server treats gps_verified as advisory (accuracy_m <= 100) so workers are never blocked from clocking in/out if the browser denies geolocation or the context is insecure. Also picks up the manual-entry / manual-logs reducer plumbing that was left over from the WIP — the actions exist in src/actions/time-tracking/field.ts (submitManualTimeLog, getRecentTimeLogs) and the reducer is wired to call them, but the HubScreen / ManualEntryScreen render paths still live in the parent screen router and consume the state correctly.
This commit is contained in:
@@ -37,9 +37,12 @@ import {
|
|||||||
getTimeTrackingTasksField,
|
getTimeTrackingTasksField,
|
||||||
getWorkerPayPeriodHours,
|
getWorkerPayPeriodHours,
|
||||||
getTimeTrackingSession,
|
getTimeTrackingSession,
|
||||||
|
submitManualTimeLog,
|
||||||
|
getRecentTimeLogs,
|
||||||
type TimeTrackingSession,
|
type TimeTrackingSession,
|
||||||
type TimeTaskField,
|
type TimeTaskField,
|
||||||
type PayPeriodHours,
|
type PayPeriodHours,
|
||||||
|
type WorkerLogRow,
|
||||||
} from "@/actions/time-tracking/field";
|
} from "@/actions/time-tracking/field";
|
||||||
import { FieldPinScreen } from "@/components/field/FieldPinScreen";
|
import { FieldPinScreen } from "@/components/field/FieldPinScreen";
|
||||||
import { Clock, CheckCircle, Spinner, ChevronLeft, ChevronRight } from "@/components/water/mobile/icons";
|
import { Clock, CheckCircle, Spinner, ChevronLeft, ChevronRight } from "@/components/water/mobile/icons";
|
||||||
@@ -126,6 +129,65 @@ type Translations = {
|
|||||||
total_time: string;
|
total_time: string;
|
||||||
generic_error: string;
|
generic_error: string;
|
||||||
forgot_hint: string;
|
forgot_hint: string;
|
||||||
|
|
||||||
|
// Cycle 12 — Hub, Manual Entry, History
|
||||||
|
hub_greeting: string;
|
||||||
|
hub_subtitle: string;
|
||||||
|
hub_logged_today: string;
|
||||||
|
hub_logged_today_none: string;
|
||||||
|
hub_manual_entry: string;
|
||||||
|
hub_manual_entry_sub: string;
|
||||||
|
hub_recent_activity: string;
|
||||||
|
hub_recent_activity_sub: string;
|
||||||
|
hub_quick_actions: string;
|
||||||
|
hub_sign_out: string;
|
||||||
|
hub_status_on: string;
|
||||||
|
hub_status_off: string;
|
||||||
|
hub_view_shift: string;
|
||||||
|
|
||||||
|
manual_title: string;
|
||||||
|
manual_subtitle: string;
|
||||||
|
manual_section_when: string;
|
||||||
|
manual_section_who: string;
|
||||||
|
manual_section_why: string;
|
||||||
|
manual_date: string;
|
||||||
|
manual_clock_in_label: string;
|
||||||
|
manual_clock_out_label: string;
|
||||||
|
manual_task: string;
|
||||||
|
manual_task_placeholder: string;
|
||||||
|
manual_lunch: string;
|
||||||
|
manual_reason: string;
|
||||||
|
manual_reason_placeholder: string;
|
||||||
|
manual_notes: string;
|
||||||
|
manual_notes_placeholder: string;
|
||||||
|
manual_submit: string;
|
||||||
|
manual_submit_help: string;
|
||||||
|
manual_submitting: string;
|
||||||
|
manual_error_future: string;
|
||||||
|
manual_error_range: string;
|
||||||
|
manual_error_reason: string;
|
||||||
|
manual_error_max: string;
|
||||||
|
manual_error_too_old: string;
|
||||||
|
manual_error_generic: string;
|
||||||
|
|
||||||
|
manual_success_title: string;
|
||||||
|
manual_success_subtitle: string;
|
||||||
|
manual_success_submitted: string;
|
||||||
|
manual_success_total: string;
|
||||||
|
manual_success_lunch: string;
|
||||||
|
manual_success_another: string;
|
||||||
|
manual_success_home: string;
|
||||||
|
|
||||||
|
history_title: string;
|
||||||
|
history_subtitle: string;
|
||||||
|
history_loading: string;
|
||||||
|
history_empty_title: string;
|
||||||
|
history_empty_sub: string;
|
||||||
|
history_total_label: string;
|
||||||
|
history_manual_badge: string;
|
||||||
|
history_today: string;
|
||||||
|
history_yesterday: string;
|
||||||
|
history_load_more: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TRANS_EN: Translations = {
|
const TRANS_EN: Translations = {
|
||||||
@@ -395,6 +457,18 @@ function reducer(state: State, action: Action): State {
|
|||||||
return state.openEntry
|
return state.openEntry
|
||||||
? { ...state, openEntry: { ...state.openEntry, elapsed_minutes: action.minutes } }
|
? { ...state, openEntry: { ...state.openEntry, elapsed_minutes: action.minutes } }
|
||||||
: state;
|
: state;
|
||||||
|
case "SET_MANUAL_DRAFT":
|
||||||
|
return { ...state, manualEntryDraft: action.value };
|
||||||
|
case "RESET_MANUAL_DRAFT":
|
||||||
|
return { ...state, manualEntryDraft: emptyManualDraft() };
|
||||||
|
case "SET_MANUAL_ENTRY_RESULT":
|
||||||
|
return { ...state, manualEntryResult: action.value };
|
||||||
|
case "SET_MANUAL_LOGS":
|
||||||
|
return { ...state, manualLogs: action.value, manualLogsLoading: false, manualLogsError: null };
|
||||||
|
case "SET_MANUAL_LOGS_LOADING":
|
||||||
|
return { ...state, manualLogsLoading: action.value };
|
||||||
|
case "SET_MANUAL_LOGS_ERROR":
|
||||||
|
return { ...state, manualLogsError: action.value, manualLogsLoading: false };
|
||||||
case "RESET_FOR_LOGOUT":
|
case "RESET_FOR_LOGOUT":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -404,6 +478,11 @@ function reducer(state: State, action: Action): State {
|
|||||||
selectedTaskId: "",
|
selectedTaskId: "",
|
||||||
lunchMinutes: 0,
|
lunchMinutes: 0,
|
||||||
notes: "",
|
notes: "",
|
||||||
|
manualEntryDraft: emptyManualDraft(),
|
||||||
|
manualEntryResult: null,
|
||||||
|
manualLogs: [],
|
||||||
|
manualLogsLoading: false,
|
||||||
|
manualLogsError: null,
|
||||||
screen: "pin",
|
screen: "pin",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user