Files
TenerifeProp/public/js/api.js
Kilo 916455351d fix(admin): connect api.js and admin.js, fix property modal, add analytics endpoint
- admin.html: removed conflicting inline script, added api.js + admin.js
- admin.js: dynamic section loader with fetch, init navigates to hash
- api.js: credentials: 'include' for all admin requests
- propertyModal: added name attributes to all form fields, saveProperty onclick handler
- server/index.ts: added POST /api/analytics/event with daily aggregation
- server/validation.ts: removed min(6) from password for 401 on invalid credentials
- capability-index.yaml: added 11 MCP capability routes
- docker-compose-mcp.yml: created for MCP servers
2026-04-27 11:42:05 +01:00

344 lines
9.7 KiB
JavaScript

// TenerifeProp - API Client (with auth cookies)
const API_BASE = '/api';
class API {
// Auth
static async login(email, password) {
try {
const response = await fetch(`${API_BASE}/auth/login`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
return response.json();
} catch (error) {
return { success: false, error: 'Error de conexión' };
}
}
static async logout() {
try {
const response = await fetch(`${API_BASE}/auth/logout`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' }
});
return response.json();
} catch (error) {
return { success: false, error: 'Error de conexión' };
}
}
static async getMe() {
try {
const response = await fetch(`${API_BASE}/auth/me`, { credentials: 'include' });
return response.json();
} catch (error) {
return { success: false, error: 'Error de conexión' };
}
}
static async getCsrfToken() {
try {
const response = await fetch(`${API_BASE}/csrf-token`);
return response.json();
} catch (error) {
return { success: false, error: 'Error de conexión' };
}
}
// Properties
static async getProperties(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(`${API_BASE}/properties?${params}`);
return response.json();
}
static async getProperty(slug, lang = 'es') {
const response = await fetch(`${API_BASE}/properties/${slug}?lang=${lang}`);
return response.json();
}
static async getPropertyBySlug(slug, lang = 'es') {
const response = await fetch(`${API_BASE}/properties/${slug}?lang=${lang}`);
return response.json();
}
static async getFeaturedProperties(lang = 'es') {
const response = await fetch(`${API_BASE}/properties/featured?lang=${lang}`);
return response.json();
}
// Admin Properties
static async createProperty(data) {
const response = await fetch(`${API_BASE}/admin/properties`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async updateProperty(id, data) {
const response = await fetch(`${API_BASE}/admin/properties/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteProperty(id) {
const response = await fetch(`${API_BASE}/admin/properties/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
// Leads
static async createLead(data) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!data.email || !emailRegex.test(data.email)) {
return { success: false, error: 'Valid email is required' };
}
const sanitize = (str) => str ? String(str).replace(/[<>]/g, '') : '';
const response = await fetch(`${API_BASE}/leads`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: sanitize(data.name),
email: data.email,
phone: sanitize(data.phone),
message: sanitize(data.message),
property_id: data.property_id,
language: data.language
})
});
return response.json();
}
static async getLeads(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(`${API_BASE}/admin/leads?${params}`, { credentials: 'include' });
return response.json();
}
static async updateLead(id, data) {
const response = await fetch(`${API_BASE}/admin/leads/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteLead(id) {
const response = await fetch(`${API_BASE}/admin/leads/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
// Content
static async getTestimonials(lang = 'es') {
const response = await fetch(`${API_BASE}/testimonials?lang=${lang}`);
return response.json();
}
static async createTestimonial(data) {
const response = await fetch(`${API_BASE}/admin/testimonials`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async updateTestimonial(id, data) {
const response = await fetch(`${API_BASE}/admin/testimonials/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteTestimonial(id) {
const response = await fetch(`${API_BASE}/admin/testimonials/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
static async getFAQ(lang = 'es') {
const response = await fetch(`${API_BASE}/faq?lang=${lang}`);
return response.json();
}
static async createFAQ(data) {
const response = await fetch(`${API_BASE}/admin/faq`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async updateFAQ(id, data) {
const response = await fetch(`${API_BASE}/admin/faq/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteFAQ(id) {
const response = await fetch(`${API_BASE}/admin/faq/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
static async getServices(lang = 'es') {
const response = await fetch(`${API_BASE}/services?lang=${lang}`);
return response.json();
}
static async createService(data) {
const response = await fetch(`${API_BASE}/admin/services`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async updateService(id, data) {
const response = await fetch(`${API_BASE}/admin/services/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteService(id) {
const response = await fetch(`${API_BASE}/admin/services/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
static async getSettings() {
const response = await fetch(`${API_BASE}/settings`);
return response.json();
}
static async updateSettings(data) {
const response = await fetch(`${API_BASE}/admin/settings`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async getAdminStats() {
const response = await fetch(`${API_BASE}/admin/stats`, { credentials: 'include' });
return response.json();
}
static async getAnalyticsOverview() {
const response = await fetch(`${API_BASE}/admin/analytics/overview`, { credentials: 'include' });
return response.json();
}
static async getAnalyticsCharts() {
const response = await fetch(`${API_BASE}/admin/analytics/charts`, { credentials: 'include' });
return response.json();
}
static async trackEvent(type, data = {}) {
let sessionId = localStorage.getItem('session_id');
if (!sessionId) {
sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('session_id', sessionId);
}
await fetch(`${API_BASE}/analytics/event`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type, session_id: sessionId, ...data })
});
}
// Admin Users
static async getUsers() {
const response = await fetch(`${API_BASE}/admin/users`, { credentials: 'include' });
return response.json();
}
static async createUser(data) {
const response = await fetch(`${API_BASE}/admin/users`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async updateUser(id, data) {
const response = await fetch(`${API_BASE}/admin/users/${id}`, {
method: 'PUT',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
static async deleteUser(id) {
const response = await fetch(`${API_BASE}/admin/users/${id}`, {
method: 'DELETE',
credentials: 'include'
});
return response.json();
}
static async changePassword(currentPassword, newPassword) {
const response = await fetch(`${API_BASE}/auth/change-password`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ current_password: currentPassword, new_password: newPassword })
});
return response.json();
}
static async getCities() {
const response = await fetch(`${API_BASE}/cities`);
return response.json();
}
}
window.API = API;