Add user selection for check-in and update API to include user ID
This commit is contained in:
@@ -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>}
|
||||||
|
|||||||
@@ -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 }) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user