Initial Commit
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_URL || '/api',
|
||||
})
|
||||
|
||||
api.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
})
|
||||
|
||||
api.interceptors.response.use(
|
||||
(res) => res,
|
||||
(err) => {
|
||||
if (err.response?.status === 401) {
|
||||
localStorage.removeItem('token')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
return Promise.reject(err)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
@@ -0,0 +1,84 @@
|
||||
export function formatDuration(seconds) {
|
||||
if (!seconds) return '--'
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.floor((seconds % 3600) / 60)
|
||||
const s = Math.floor(seconds % 60)
|
||||
if (h > 0) return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
|
||||
return `${m}:${String(s).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
export function formatPace(speedMs, sportType = 'running') {
|
||||
if (!speedMs || speedMs <= 0) return '--'
|
||||
if (sportType === 'cycling') {
|
||||
const kph = speedMs * 3.6
|
||||
return `${kph.toFixed(1)} km/h`
|
||||
}
|
||||
const secsPerKm = 1000 / speedMs
|
||||
const mins = Math.floor(secsPerKm / 60)
|
||||
const secs = Math.floor(secsPerKm % 60)
|
||||
return `${mins}:${String(secs).padStart(2, '0')} /km`
|
||||
}
|
||||
|
||||
export function formatDistance(metres) {
|
||||
if (!metres) return '--'
|
||||
if (metres >= 1000) return `${(metres / 1000).toFixed(2)} km`
|
||||
return `${Math.round(metres)} m`
|
||||
}
|
||||
|
||||
export function formatElevation(metres) {
|
||||
if (metres == null) return '--'
|
||||
return `${Math.round(metres)} m`
|
||||
}
|
||||
|
||||
export function formatHeartRate(bpm) {
|
||||
if (!bpm) return '--'
|
||||
return `${Math.round(bpm)} bpm`
|
||||
}
|
||||
|
||||
export function formatSleep(seconds) {
|
||||
if (!seconds) return '--'
|
||||
const h = Math.floor(seconds / 3600)
|
||||
const m = Math.round((seconds % 3600) / 60)
|
||||
return `${h}h ${m}m`
|
||||
}
|
||||
|
||||
export function formatWeight(kg) {
|
||||
if (!kg) return '--'
|
||||
return `${kg.toFixed(1)} kg`
|
||||
}
|
||||
|
||||
export function formatDate(dateStr) {
|
||||
if (!dateStr) return '--'
|
||||
return new Date(dateStr).toLocaleDateString('en-GB', {
|
||||
day: 'numeric', month: 'short', year: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
export function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '--'
|
||||
return new Date(dateStr).toLocaleDateString('en-GB', {
|
||||
day: 'numeric', month: 'short', year: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
})
|
||||
}
|
||||
|
||||
export function hrZoneColor(zone) {
|
||||
const colors = { z1: '#60a5fa', z2: '#34d399', z3: '#fbbf24', z4: '#f97316', z5: '#f43f5e' }
|
||||
return colors[zone] || '#9ca3af'
|
||||
}
|
||||
|
||||
export function sportIcon(sportType) {
|
||||
const icons = {
|
||||
running: '🏃', cycling: '🚴', swimming: '🏊', hiking: '🥾',
|
||||
walking: '🚶', other: '⚡',
|
||||
}
|
||||
return icons[sportType?.toLowerCase()] || '⚡'
|
||||
}
|
||||
|
||||
export function sportColor(sportType) {
|
||||
const colors = {
|
||||
running: '#3b82f6', cycling: '#f97316', swimming: '#06b6d4',
|
||||
hiking: '#84cc16', walking: '#a78bfa', other: '#6b7280',
|
||||
}
|
||||
return colors[sportType?.toLowerCase()] || '#6b7280'
|
||||
}
|
||||
Reference in New Issue
Block a user