Enhance API clients with auto-logout on unauthorized responses; improve navigation and schedule handling with better error management and UI updates
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
+28
-4
@@ -16,7 +16,31 @@ const AUTH_API = normalizeApiBaseUrl(RUNTIME.AUTH_API || import.meta.env.VITE_AU
|
||||
const TRANSIT_API = normalizeApiBaseUrl(RUNTIME.TRANSIT_API || import.meta.env.VITE_TRANSIT_API, 'http://localhost:5001')
|
||||
|
||||
export const authClient = axios.create({ baseURL: AUTH_API })
|
||||
export const transitClient = (token) => axios.create({
|
||||
baseURL: TRANSIT_API,
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
|
||||
export const transitClient = (token) => {
|
||||
const client = axios.create({
|
||||
baseURL: TRANSIT_API,
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
|
||||
// Auto-logout on unauthorized responses to avoid delayed failures
|
||||
client.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
try {
|
||||
const status = error?.response?.status
|
||||
if (status === 401 || status === 403) {
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('user')
|
||||
// reload to let App show the Login screen
|
||||
if (typeof window !== 'undefined') window.location.reload()
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -22,6 +22,27 @@ export default function FleetManager({ token, user }) {
|
||||
}
|
||||
}
|
||||
|
||||
const overlay = {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
zIndex: 1000,
|
||||
}
|
||||
|
||||
const modal = {
|
||||
backgroundColor: '#fff',
|
||||
padding: '2rem',
|
||||
borderRadius: '8px',
|
||||
maxWidth: '400px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadBuses()
|
||||
}, [])
|
||||
|
||||
@@ -424,18 +424,34 @@ export default function ScheduleMaker({ token, user }) {
|
||||
<td style={{ flex: 1 }}>{rs.name}</td>
|
||||
<td style={{ textAlign: 'right', color: '#666' }}>{formatTime(departureDate.toISOString())}</td>
|
||||
<td style={{ paddingLeft: '0.5rem' }}>
|
||||
<input
|
||||
type="number"
|
||||
value={rs.estimatedArrivalMinutes ?? 0}
|
||||
onChange={e => {
|
||||
const val = e.target.value === '' ? '' : Number(e.target.value)
|
||||
setRouteStopsByLine(prev => ({
|
||||
...prev,
|
||||
[lineId]: prev[lineId].map(item => item.id === rs.id ? { ...item, estimatedArrivalMinutes: val } : item)
|
||||
}))
|
||||
}}
|
||||
style={{ width: '5rem' }}
|
||||
/>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="240"
|
||||
value={Number(rs.estimatedArrivalMinutes ?? 0)}
|
||||
onChange={e => {
|
||||
const val = Number(e.target.value)
|
||||
setRouteStopsByLine(prev => ({
|
||||
...prev,
|
||||
[lineId]: prev[lineId].map(item => item.id === rs.id ? { ...item, estimatedArrivalMinutes: val } : item)
|
||||
}))
|
||||
}}
|
||||
style={{ width: '8rem' }}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
value={Number(rs.estimatedArrivalMinutes ?? 0)}
|
||||
onChange={e => {
|
||||
const val = e.target.value === '' ? 0 : Number(e.target.value)
|
||||
setRouteStopsByLine(prev => ({
|
||||
...prev,
|
||||
[lineId]: prev[lineId].map(item => item.id === rs.id ? { ...item, estimatedArrivalMinutes: val } : item)
|
||||
}))
|
||||
}}
|
||||
style={{ width: '4.5rem' }}
|
||||
/>
|
||||
</div>
|
||||
<button type="button" onClick={async () => {
|
||||
const newVal = Number((routeStopsByLine[lineId].find(i => i.id === rs.id)?.estimatedArrivalMinutes) || 0)
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user