fase 2.3: portal publico de reservas en linea (5 endpoints anonimos con rate limit y token HMAC, wizard movil, gestion/cancelacion por enlace, distintivo en agenda, editor y enlace publico en Configuracion); apagado por defecto
This commit is contained in:
@@ -72,6 +72,8 @@ export interface Appointment {
|
||||
doctor?: string;
|
||||
branch?: string;
|
||||
medium?: string;
|
||||
/** Origen de la reserva: 'internal' (default) o 'public_portal' (portal en línea) */
|
||||
booking_source?: string;
|
||||
notes?: string;
|
||||
package_finished?: boolean;
|
||||
package_finished_date?: string | null;
|
||||
@@ -1010,6 +1012,106 @@ export interface ClinicSettings {
|
||||
moneda_base: string;
|
||||
nota_ticket: string;
|
||||
impuestos: ImpuestosSettings;
|
||||
reservas_online: ReservasOnlineSettings;
|
||||
}
|
||||
|
||||
// ---- Reservas en línea (portal público de auto-agendado) ----
|
||||
export interface ReservasOnlineSettings {
|
||||
activo: boolean;
|
||||
titulo: string;
|
||||
descripcion: string;
|
||||
requiere_confirmacion: boolean;
|
||||
permitir_eleccion_doctor: boolean;
|
||||
permitir_sucursal: boolean;
|
||||
max_dias_futuro: number;
|
||||
min_horas_anticipacion: number;
|
||||
permitir_cancelar: boolean;
|
||||
cancelar_hasta_horas: number;
|
||||
mensaje_confirmacion: string;
|
||||
telefono_contacto: string;
|
||||
}
|
||||
|
||||
export const RESERVAS_ONLINE_DEFAULT: ReservasOnlineSettings = {
|
||||
activo: false,
|
||||
titulo: 'Reserva tu cita en línea',
|
||||
descripcion: '',
|
||||
requiere_confirmacion: true,
|
||||
permitir_eleccion_doctor: true,
|
||||
permitir_sucursal: false,
|
||||
max_dias_futuro: 60,
|
||||
min_horas_anticipacion: 2,
|
||||
permitir_cancelar: true,
|
||||
cancelar_hasta_horas: 24,
|
||||
mensaje_confirmacion: '',
|
||||
telefono_contacto: '',
|
||||
};
|
||||
|
||||
// Contrato del portal público (endpoints /public/booking/*)
|
||||
export interface BookingConfig {
|
||||
activo: boolean;
|
||||
brand: ClinicBrand;
|
||||
terms: GlossaryTerms;
|
||||
sucursales: { code: string; name: string; address?: string; phone?: string }[];
|
||||
categorias: { code: string; name: string }[];
|
||||
servicios: { id: number; nombre: string; categoria: string; precio: number; duracion_min: number }[];
|
||||
medicos: { id: number; nombre: string; staff_type: string }[];
|
||||
config: {
|
||||
max_dias_futuro: number;
|
||||
min_horas_anticipacion: number;
|
||||
permite_sucursal: boolean;
|
||||
permite_eleccion_doctor: boolean;
|
||||
requiere_confirmacion: boolean;
|
||||
permitir_cancelar: boolean;
|
||||
cancelar_hasta_horas: number;
|
||||
telefono_contacto: string;
|
||||
titulo: string;
|
||||
descripcion: string;
|
||||
mensaje_confirmacion: string;
|
||||
zona_horaria?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BookingCreatePayload {
|
||||
servicio_id: number;
|
||||
fecha: string;
|
||||
hora: string;
|
||||
medico_id?: number | null;
|
||||
sucursal?: string | null;
|
||||
nombre: string;
|
||||
telefono: string;
|
||||
email?: string;
|
||||
notas?: string;
|
||||
hp?: string;
|
||||
}
|
||||
|
||||
export interface BookingCreateResult {
|
||||
ok: boolean;
|
||||
folio: string;
|
||||
fecha: string;
|
||||
hora: string;
|
||||
medico: string | null;
|
||||
servicio: string;
|
||||
sucursal: string;
|
||||
estado: string;
|
||||
requiere_confirmacion: boolean;
|
||||
mensaje_confirmacion: string;
|
||||
token_de_gestion: string;
|
||||
}
|
||||
|
||||
export interface BookingCita {
|
||||
status: string;
|
||||
folio: string;
|
||||
fecha: string;
|
||||
hora: string;
|
||||
servicio: string;
|
||||
medico: string | null;
|
||||
sucursal: string;
|
||||
estado: string;
|
||||
estado_label: string;
|
||||
puede_cancelar: boolean;
|
||||
permitir_cancelar: boolean;
|
||||
cancelar_hasta_horas: number;
|
||||
telefono_contacto: string;
|
||||
}
|
||||
|
||||
export const CLINIC_SETTINGS_DEFAULT: ClinicSettings = {
|
||||
@@ -1026,6 +1128,7 @@ export const CLINIC_SETTINGS_DEFAULT: ClinicSettings = {
|
||||
moneda_base: 'MXN',
|
||||
nota_ticket: '',
|
||||
impuestos: IMPUESTOS_DEFAULT,
|
||||
reservas_online: RESERVAS_ONLINE_DEFAULT,
|
||||
};
|
||||
|
||||
export interface FrontendUser {
|
||||
@@ -1686,6 +1789,32 @@ export const odooApi = {
|
||||
return data;
|
||||
},
|
||||
|
||||
// ---- Portal público de reservas en línea (sin autenticación) ----
|
||||
async getBookingConfig(): Promise<BookingConfig> {
|
||||
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/public/booking/config`);
|
||||
return data;
|
||||
},
|
||||
|
||||
async getBookingSlots(params: { servicio_id: number; fecha: string; medico_id?: number | null; sucursal?: string | null }): Promise<{ status: string; slots: string[]; medico_id: number | null; duracion_min: number; message?: string }> {
|
||||
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/public/booking/slots`, { params });
|
||||
return data;
|
||||
},
|
||||
|
||||
async createBooking(payload: BookingCreatePayload): Promise<BookingCreateResult> {
|
||||
const { data } = await axios.post(`${ODOO_BASE}/skeen/frontend/v1/public/booking/create`, payload);
|
||||
return data;
|
||||
},
|
||||
|
||||
async getBookingCita(token: string): Promise<BookingCita> {
|
||||
const { data } = await axios.get(`${ODOO_BASE}/skeen/frontend/v1/public/booking/cita`, { params: { token } });
|
||||
return data;
|
||||
},
|
||||
|
||||
async cancelBooking(token: string, motivo?: string): Promise<{ status: string; ok?: boolean; folio?: string; estado?: string; message?: string }> {
|
||||
const { data } = await axios.post(`${ODOO_BASE}/skeen/frontend/v1/public/booking/cancel`, { token, motivo });
|
||||
return data;
|
||||
},
|
||||
|
||||
async updateRolePermissions(matrix: Record<string, FrontendRole[]>): Promise<{ status: string; matrix: Record<string, FrontendRole[]>; message?: string }> {
|
||||
const { data } = await axios.put(`${ODOO_BASE}/skeen/frontend/v1/roles/permissions`, { matrix });
|
||||
return data;
|
||||
|
||||
Reference in New Issue
Block a user