feat: initial commit — HermesMessages SaaS platform
Backend (FastAPI + Python 3.12):
- Multi-tenant auth with JWT: login, register, refresh, Meta OAuth
- Business & BusinessConfig management
- WhatsApp webhook with HMAC signature verification
- Bot engine powered by Claude AI
- Calendar availability with Redis caching
- Reservations CRUD with status management
- Dashboard analytics (stats, agenda, peak hours)
- Billing & plan management
- Admin panel with platform-wide stats
- Async bcrypt via asyncio.to_thread
- IntegrityError handling for concurrent registration race conditions
Frontend (React 18 + Vite + Tailwind CSS):
- Multi-step guided registration form with helper text on every field
- Login page with show/hide password toggle
- Protected routes with AuthContext
- Dashboard with stats cards, bar chart, and daily agenda
- Reservations list with search, filters, and inline status actions
- Calendar with weekly view, slot availability, and date blocking
- Config page: business info, schedules, bot personality
- Billing page with plan comparison and usage bar
Design system:
- Bricolage Grotesque + DM Sans typography
- Emerald primary palette with semantic color tokens
- scale(0.97) button press feedback, ease-out animations
- Skeleton loaders, stagger animations, prefers-reduced-motion support
- Accessible: aria-labels, visible focus rings, 4.5:1 contrast
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:49:41 -05:00
|
|
|
import axios from 'axios'
|
|
|
|
|
|
|
|
|
|
const api = axios.create({
|
|
|
|
|
baseURL: '/api',
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
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 const authApi = {
|
|
|
|
|
register: (data) => api.post('/auth/register', data),
|
|
|
|
|
login: (data) => api.post('/auth/login', data),
|
|
|
|
|
logout: () => api.post('/auth/logout'),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const businessApi = {
|
|
|
|
|
getMe: () => api.get('/business/me'),
|
|
|
|
|
updateMe: (data) => api.put('/business/me', data),
|
|
|
|
|
getConfig: () => api.get('/business/me/config'),
|
|
|
|
|
updateConfig: (data) => api.put('/business/me/config', data),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const whatsappApi = {
|
|
|
|
|
getStatus: () => api.get('/whatsapp/status'),
|
|
|
|
|
connect: (data) => api.post('/whatsapp/connect', data),
|
|
|
|
|
disconnect: () => api.post('/whatsapp/disconnect'),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const reservationsApi = {
|
|
|
|
|
list: (params) => api.get('/reservations/', { params }),
|
|
|
|
|
create: (data) => api.post('/reservations/', data),
|
|
|
|
|
get: (id) => api.get(`/reservations/${id}`),
|
|
|
|
|
update: (id, data) => api.put(`/reservations/${id}`, data),
|
|
|
|
|
updateStatus: (id, status) => api.patch(`/reservations/${id}/status`, { status }),
|
|
|
|
|
delete: (id) => api.delete(`/reservations/${id}`),
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-29 09:39:56 -05:00
|
|
|
export const serviceApi = {
|
|
|
|
|
list: () => api.get('/business/me/services'),
|
|
|
|
|
create: (data) => api.post('/business/me/services', data),
|
|
|
|
|
update: (id, data) => api.put(`/business/me/services/${id}`, data),
|
|
|
|
|
remove: (id) => api.delete(`/business/me/services/${id}`),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const tableApi = {
|
|
|
|
|
list: () => api.get('/business/me/tables'),
|
|
|
|
|
create: (data) => api.post('/business/me/tables', data),
|
|
|
|
|
update: (id, data) => api.put(`/business/me/tables/${id}`, data),
|
|
|
|
|
remove: (id) => api.delete(`/business/me/tables/${id}`),
|
|
|
|
|
}
|
|
|
|
|
|
feat: initial commit — HermesMessages SaaS platform
Backend (FastAPI + Python 3.12):
- Multi-tenant auth with JWT: login, register, refresh, Meta OAuth
- Business & BusinessConfig management
- WhatsApp webhook with HMAC signature verification
- Bot engine powered by Claude AI
- Calendar availability with Redis caching
- Reservations CRUD with status management
- Dashboard analytics (stats, agenda, peak hours)
- Billing & plan management
- Admin panel with platform-wide stats
- Async bcrypt via asyncio.to_thread
- IntegrityError handling for concurrent registration race conditions
Frontend (React 18 + Vite + Tailwind CSS):
- Multi-step guided registration form with helper text on every field
- Login page with show/hide password toggle
- Protected routes with AuthContext
- Dashboard with stats cards, bar chart, and daily agenda
- Reservations list with search, filters, and inline status actions
- Calendar with weekly view, slot availability, and date blocking
- Config page: business info, schedules, bot personality
- Billing page with plan comparison and usage bar
Design system:
- Bricolage Grotesque + DM Sans typography
- Emerald primary palette with semantic color tokens
- scale(0.97) button press feedback, ease-out animations
- Skeleton loaders, stagger animations, prefers-reduced-motion support
- Accessible: aria-labels, visible focus rings, 4.5:1 contrast
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:49:41 -05:00
|
|
|
export const calendarApi = {
|
|
|
|
|
getAvailability: (date) => api.get('/calendar/availability', { params: { date } }),
|
|
|
|
|
getAvailabilityRange: (start, end) =>
|
|
|
|
|
api.get('/calendar/availability/range', { params: { start_date: start, end_date: end } }),
|
|
|
|
|
blockDate: (date) => api.post('/calendar/blocked-dates', { date }),
|
|
|
|
|
unblockDate: (date) => api.delete(`/calendar/blocked-dates/${date}`),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const dashboardApi = {
|
|
|
|
|
getStats: () => api.get('/dashboard/stats'),
|
|
|
|
|
getAgenda: (date) => api.get('/dashboard/agenda', { params: { date } }),
|
|
|
|
|
getPeakHours: () => api.get('/dashboard/peak-hours'),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const billingApi = {
|
|
|
|
|
getPlan: () => api.get('/billing/plan'),
|
|
|
|
|
getUsage: () => api.get('/billing/usage'),
|
|
|
|
|
upgrade: (plan) => api.post('/billing/upgrade', { plan }),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default api
|