fix: Connect admin dashboard to real backend API with authentication

- Fixed CORS to allow pfadmin.noru1.ro and localhost:3335
- Fixed API client to handle nested token response structure (data.tokens.accessToken)
- Added deviceInfo requirement to login endpoint
- Fixed API endpoint paths to use /api/v1 prefix consistently
- Updated admin user password to 'admin123' for demo@parentflowapp.com
- Fixed Grid deprecation warnings by replacing with CSS Grid
- Added automatic redirect to /login on 401 unauthorized
- Enhanced user management service to include familyCount, childrenCount, deviceCount
- Backend now queries family_members, children, and device_registry tables for counts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andrei
2025-10-07 15:44:59 +00:00
parent 5ddb8222bf
commit 3c934c300a
6 changed files with 257 additions and 162 deletions

View File

@@ -35,7 +35,7 @@ class ApiClient {
private async request(method: string, endpoint: string, data?: any, options?: any) {
const config = {
method,
url: `${API_BASE_URL}/api/v1${endpoint}`,
url: `${API_BASE_URL}${endpoint}`,
headers: {
'Content-Type': 'application/json',
...(this.token ? { Authorization: `Bearer ${this.token}` } : {}),
@@ -55,7 +55,7 @@ class ApiClient {
// Handle token refresh
if (error.response?.status === 401 && this.refreshToken) {
try {
const refreshResponse = await axios.post(`${API_BASE_URL}/api/v1/auth/refresh`, {
const refreshResponse = await axios.post(`${API_BASE_URL}/auth/refresh`, {
refreshToken: this.refreshToken,
});
@@ -75,23 +75,73 @@ class ApiClient {
}
}
// Generic HTTP methods
async get(endpoint: string, options?: any) {
return this.request('GET', endpoint, undefined, options);
}
async post(endpoint: string, data?: any, options?: any) {
return this.request('POST', endpoint, data, options);
}
async patch(endpoint: string, data?: any, options?: any) {
return this.request('PATCH', endpoint, data, options);
}
async put(endpoint: string, data?: any, options?: any) {
return this.request('PUT', endpoint, data, options);
}
async delete(endpoint: string, options?: any) {
return this.request('DELETE', endpoint, undefined, options);
}
// Auth endpoints
async login(email: string, password: string) {
const response = await this.request('POST', '/admin/auth/login', { email, password });
this.setTokens(response.accessToken, response.refreshToken);
// Generate device info for admin dashboard
const deviceInfo = {
deviceId: this.getOrCreateDeviceId(),
platform: 'web',
model: 'Admin Dashboard',
osVersion: navigator.userAgent,
};
const response = await this.request('POST', '/auth/login', {
email,
password,
deviceInfo
});
// Extract tokens from nested response structure
const tokens = response.tokens || response.data?.tokens;
if (tokens?.accessToken && tokens?.refreshToken) {
this.setTokens(tokens.accessToken, tokens.refreshToken);
}
return response;
}
private getOrCreateDeviceId(): string {
if (typeof window === 'undefined') return 'server';
let deviceId = localStorage.getItem('admin_device_id');
if (!deviceId) {
deviceId = 'admin_' + Math.random().toString(36).substring(2) + Date.now().toString(36);
localStorage.setItem('admin_device_id', deviceId);
}
return deviceId;
}
async logout() {
try {
await this.request('POST', '/admin/auth/logout');
await this.request('POST', '/auth/logout');
} finally {
this.clearTokens();
}
}
async getCurrentAdmin() {
return this.request('GET', '/admin/auth/me');
return this.request('GET', '/auth/me');
}
// User management endpoints