Add user selection for check-in and update API to include user ID

This commit is contained in:
2026-09-04 12:26:59 +05:30
parent 51aaed8fd1
commit dfd51b8273
2 changed files with 48 additions and 17 deletions
@@ -13,8 +13,8 @@ import {
SelectValue, SelectValue,
} from '@/components/ui/select' } from '@/components/ui/select'
import { toast } from '@/components/ui/toast' import { toast } from '@/components/ui/toast'
import { routes as routesApi, stores as storesApi, getApiError, getPaginatedItems } from '@/lib/api/index' import { routes as routesApi, stores as storesApi, users as usersApi, getApiError, getPaginatedItems } from '@/lib/api/index'
import type { RouteResponse, StoreResponse, RouteVisitResponse } from '@/types/api' import type { RouteResponse, StoreResponse, RouteVisitResponse, UserResponse } from '@/types/api'
type VisitState = 'idle' | 'checked-in' | 'checked-out' type VisitState = 'idle' | 'checked-in' | 'checked-out'
@@ -36,11 +36,16 @@ export function SalesVisitsClient() {
const [visitState, setVisitState] = useState<Record<string, VisitState>>({}) const [visitState, setVisitState] = useState<Record<string, VisitState>>({})
const [deviationByStore, setDeviationByStore] = useState<Record<string, boolean>>({}) const [deviationByStore, setDeviationByStore] = useState<Record<string, boolean>>({})
const [busyStoreId, setBusyStoreId] = useState<string | null>(null) const [busyStoreId, setBusyStoreId] = useState<string | null>(null)
const [usersList, setUsersList] = useState<UserResponse[]>([])
const [checkedInByUserId, setCheckedInByUserId] = useState('')
useEffect(() => { useEffect(() => {
routesApi.list({ page: 1, pageSize: 200 }) routesApi.list({ page: 1, pageSize: 200 })
.then((res) => setRouteList(getPaginatedItems(res).filter((r) => r.isActive))) .then((res) => setRouteList(getPaginatedItems(res).filter((r) => r.isActive)))
.catch(() => toast.add({ title: 'Failed to load routes', type: 'error' })) .catch(() => toast.add({ title: 'Failed to load routes', type: 'error' }))
usersApi.list({ pageSize: 200 })
.then((res) => setUsersList(getPaginatedItems(res)))
.catch(() => toast.add({ title: 'Failed to load users', type: 'error' }))
}, []) }, [])
useEffect(() => { useEffect(() => {
@@ -60,6 +65,10 @@ export function SalesVisitsClient() {
}, [selectedRouteId]) }, [selectedRouteId])
async function handleCheckIn(store: StoreResponse) { async function handleCheckIn(store: StoreResponse) {
if (!checkedInByUserId) {
toast.add({ title: 'Select who is checking in first', type: 'error' })
return
}
setBusyStoreId(store.id) setBusyStoreId(store.id)
try { try {
const position = await getCurrentPosition() const position = await getCurrentPosition()
@@ -67,6 +76,7 @@ export function SalesVisitsClient() {
storeId: store.id, storeId: store.id,
latitude: position.coords.latitude, latitude: position.coords.latitude,
longitude: position.coords.longitude, longitude: position.coords.longitude,
checkedInByUserId,
}) })
setVisitState((prev) => ({ ...prev, [store.id]: 'checked-in' })) setVisitState((prev) => ({ ...prev, [store.id]: 'checked-in' }))
setDeviationByStore((prev) => ({ ...prev, [store.id]: result.isDeviationFlagged })) setDeviationByStore((prev) => ({ ...prev, [store.id]: result.isDeviationFlagged }))
@@ -105,20 +115,41 @@ export function SalesVisitsClient() {
</p> </p>
</div> </div>
<div className="flex flex-col gap-2 max-w-sm"> <div className="flex flex-wrap gap-4">
<Label>Route</Label> <div className="flex flex-col gap-2 max-w-sm">
<Select value={selectedRouteId} onValueChange={(v) => setSelectedRouteId(v ?? '')}> <Label>Route</Label>
<SelectTrigger> <Select value={selectedRouteId} onValueChange={(v) => setSelectedRouteId(v ?? '')}>
<SelectValue placeholder="Select a route"> <SelectTrigger>
{routeList.find((r) => r.id === selectedRouteId)?.routeName} <SelectValue placeholder="Select a route">
</SelectValue> {routeList.find((r) => r.id === selectedRouteId)?.routeName}
</SelectTrigger> </SelectValue>
<SelectContent> </SelectTrigger>
{routeList.map((r) => ( <SelectContent>
<SelectItem key={r.id} value={r.id}>{r.routeName}</SelectItem> {routeList.map((r) => (
))} <SelectItem key={r.id} value={r.id}>{r.routeName}</SelectItem>
</SelectContent> ))}
</Select> </SelectContent>
</Select>
</div>
<div className="flex flex-col gap-2 max-w-sm">
<Label>Checking In As</Label>
<Select value={checkedInByUserId} onValueChange={(v) => setCheckedInByUserId(v ?? '')}>
<SelectTrigger>
<SelectValue placeholder="Select user">
{usersList.find((u) => u.id === checkedInByUserId)?.fullName}
</SelectValue>
</SelectTrigger>
<SelectContent>
{usersList.map((u) => (
<SelectItem key={u.id} value={u.id}>{u.fullName}</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
Temporary — required until auth resolves the logged-in user automatically.
</p>
</div>
</div> </div>
{loadingStores && <p className="text-sm text-muted-foreground">Loading stores…</p>} {loadingStores && <p className="text-sm text-muted-foreground">Loading stores…</p>}
+1 -1
View File
@@ -156,7 +156,7 @@ export const routes = {
assignOwner: (id: string, salesRepId: string) => assignOwner: (id: string, salesRepId: string) =>
api.put<RouteResponse>(`/routes/${id}/owner`, { salesRepId }).then(r => r.data), api.put<RouteResponse>(`/routes/${id}/owner`, { salesRepId }).then(r => r.data),
checkIn: (id: string, body: { storeId: string; latitude: number; longitude: number }) => checkIn: (id: string, body: { storeId: string; latitude: number; longitude: number; checkedInByUserId: string }) =>
api.post<RouteVisitResponse>(`/routes/${id}/visits/check-in`, body).then(r => r.data), api.post<RouteVisitResponse>(`/routes/${id}/visits/check-in`, body).then(r => r.data),
checkOut: (id: string, body: { storeId: string }) => checkOut: (id: string, body: { storeId: string }) =>