feat: Phase 2 — auth, node/edge modals, API wiring

- Login page: full-screen dark, grid bg, JWT flow
- Auth store (Zustand) + API client (axios + interceptors)
- NodeModal: add/edit form (type, label, hostname, IP, check method)
- EdgeModal: link type picker + VLAN ID + label on connect
- App: auth gate, canvas load from API (fallback to demo), Ctrl+S save
- CanvasContainer: expose onConnect prop for edge modal flow
This commit is contained in:
Pouzor
2026-03-06 23:33:55 +01:00
parent 9bb34c99cc
commit 150302c3f7
10 changed files with 776 additions and 29 deletions
+42
View File
@@ -0,0 +1,42 @@
import axios from 'axios'
import { useAuthStore } from '@/stores/authStore'
export const api = axios.create({
baseURL: '/api/v1',
})
api.interceptors.request.use((config) => {
const token = useAuthStore.getState().token
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
api.interceptors.response.use(
(r) => r,
(err) => {
if (err.response?.status === 401) useAuthStore.getState().logout()
return Promise.reject(err)
}
)
export const authApi = {
login: (username: string, password: string) =>
api.post<{ access_token: string }>('/auth/login', { username, password }),
}
export const canvasApi = {
load: () => api.get('/canvas'),
save: (payload: { node_positions: { id: string; x: number; y: number }[]; viewport: object }) =>
api.post('/canvas/save', payload),
}
export const nodesApi = {
create: (data: object) => api.post('/nodes', data),
update: (id: string, data: object) => api.patch(`/nodes/${id}`, data),
delete: (id: string) => api.delete(`/nodes/${id}`),
}
export const edgesApi = {
create: (data: object) => api.post('/edges', data),
delete: (id: string) => api.delete(`/edges/${id}`),
}