Este sistema permite añadir una funcionalidad de reservas personalizadas a productos en WooCommerce. Con esta integración, se pueden configurar horarios específicos de disponibilidad, días festivos no reservables y bloques de tiempo para gestionar las reservas de forma eficiente. Además, los clientes pueden seleccionar fecha y hora directamente desde la página del producto, y los administradores pueden gestionar todas las reservas desde el panel de control de WordPress.
El objetivo principal de este código es proporcionar una solución práctica y flexible para negocios que ofrecen servicios basados en reservas, como consultorías, alquileres o actividades programadas, optimizando la interacción entre clientes y administradores.
Explicación de las Funciones
1. Agregar Opciones de Configuración para Productos Reservables
- Se introduce una sección adicional en la página de configuración del producto en WooCommerce.
- Permite marcar un producto como reservable y configurar opciones como horarios disponibles, días festivos y duración de los bloques de reserva.
2. Visualización de Horarios Disponibles
- Se genera dinámicamente una tabla interactiva para que los administradores ingresen los horarios disponibles por día.
- Los horarios se almacenan en un formato JSON para facilitar su manipulación y uso.
3. Gestión de Días Festivos
- Se habilita un campo para ingresar fechas específicas (en formato
YYYY-MM-DD) como días no disponibles para reservas. - Estas fechas se excluyen automáticamente del selector de fechas para los clientes.

4. Configuración de Duración de Bloques de Reserva
- Se permite seleccionar la duración de los bloques de reserva (15, 30, 60 o 120 minutos).
- Esto determina los intervalos de tiempo que estarán disponibles para los clientes.
5. Selector de Fecha y Hora en la Página del Producto
- Los clientes pueden elegir una fecha y hora para su reserva mediante un formulario en la página del producto.
- La selección se limita a las fechas y horarios disponibles según la configuración del administrador.

6. Validación Antes de Añadir al Carrito
- Se verifica que la fecha y hora seleccionadas no estén reservadas antes de permitir que el cliente añada el producto al carrito.
- En caso de conflicto, se muestra un mensaje de error.
7. Almacenamiento de Reservas en el Carrito y el Pedido
- Las fechas y horas seleccionadas se guardan como metadatos del producto en el carrito y en el pedido.
- Esto asegura que la información de la reserva se mantenga durante todo el proceso de compra.
8. Gestión de Reservas en el Panel de Administración
- Se añade una nueva página en el panel de administración de WordPress para visualizar, cancelar y eliminar reservas.
- La página utiliza DataTables para una experiencia más interactiva, con opciones de búsqueda y ordenamiento.

9. Estilos Personalizados para Formularios de Reserva
- Se aplican estilos modernos y responsivos a los formularios de selección de fecha y hora para mejorar la experiencia del usuario.
10. Reemplazo del Botón «Añadir al Carrito»
- Para productos reservables, el botón estándar se reemplaza con un enlace que redirige a la página del producto para seleccionar una fecha y hora.
11. Gestión de Reservas Existentes
- Una función verifica las reservas existentes para evitar conflictos de horarios.
- También se proporciona la opción de cancelar o eliminar reservas desde la página de administración.
12. Funciones Auxiliares
- Obtener Reservas Existentes: Recupera todas las reservas asociadas a un producto desde los pedidos.
- Validar Fechas y Horarios: Se asegura de que las selecciones respeten las configuraciones de horarios y días festivos.
- Conversión de Días al Español: Traduce los nombres de días al español para la presentación en el frontend.
El siguiente código tienes que agregarlo en el functions.php de tu tema hijo o utilizar un plugin como CODE SNIPPETS.
// Mostrar opciones de configuración en el producto
add_action('woocommerce_product_options_general_product_data', function() {
global $post;
// Obtener los valores guardados
$horarios_guardados = get_post_meta($post->ID, '_horario_disponible', true);
$horarios_guardados = $horarios_guardados ? json_decode($horarios_guardados, true) : [];
$festivos_guardados = get_post_meta($post->ID, '_dias_festivos', true);
echo '<div class="options_group">';
// Producto reservable
woocommerce_wp_checkbox([
'id' => '_es_reservable',
'label' => __('Es Reservable', 'woocommerce'),
'description' => __('Haz que este producto sea reservable en horarios específicos.', 'woocommerce')
]);
// Tabla de horarios disponibles
echo '<label for="_horario_disponible_table">' . __('Horarios Disponibles', 'woocommerce') . '</label>';
echo '<div id="_horario_disponible_table" class="styled-horarios-table"></div>';
echo '<textarea id="_horario_disponible" name="_horario_disponible" style="display:none;">' . esc_textarea(json_encode($horarios_guardados)) . '</textarea>';
echo '<p class="description">' . __('Edita los horarios en la tabla. Ejemplo: Lunes 08:00-14:00,16:00-20:00.', 'woocommerce') . '</p>';
// Campo para días festivos
woocommerce_wp_text_input([
'id' => '_dias_festivos',
'label' => __('Días Festivos', 'woocommerce'),
'description' => __('Ingresa las fechas de días festivos en formato YYYY-MM-DD separadas por comas.', 'woocommerce'),
'desc_tip' => true,
'value' => esc_attr($festivos_guardados)
]);
// Duración de bloques
woocommerce_wp_select([
'id' => '_duracion_bloque_reserva',
'label' => __('Duración de Bloques', 'woocommerce'),
'description' => __('Selecciona la duración de cada bloque de reserva.', 'woocommerce'),
'options' => [
'15' => __('15 Minutos', 'woocommerce'),
'30' => __('30 Minutos', 'woocommerce'),
'60' => __('1 Hora', 'woocommerce'),
'120' => __('2 Horas', 'woocommerce')
]
]);
echo '</div>';
// Estilos CSS para la tabla
echo '<style>
.styled-horarios-table table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
margin-bottom: 10px;
}
.styled-horarios-table th {
background-color: #f4f4f4;
font-weight: bold;
text-align: left;
padding: 8px;
border: 1px solid #ccc;
}
.styled-horarios-table td {
padding: 8px;
border: 1px solid #ccc;
vertical-align: middle;
}
.styled-horarios-table td input {
width: 100%;
padding: 6px;
font-size: 14px;
}
.styled-horarios-table tr:nth-child(even) {
background-color: #f9f9f9;
}
.styled-horarios-table tr:hover {
background-color: #f1f1f1;
}
</style>';
// Script para generar la tabla
echo '<script>
document.addEventListener("DOMContentLoaded", function() {
const days = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"];
const horarios = document.querySelector("#_horario_disponible").value ? JSON.parse(document.querySelector("#_horario_disponible").value) : {};
const tableContainer = document.querySelector("#_horario_disponible_table");
const createTable = () => {
const table = document.createElement("table");
const headerRow = document.createElement("tr");
const dayHeader = document.createElement("th");
dayHeader.textContent = "Día";
const horarioHeader = document.createElement("th");
horarioHeader.textContent = "Horarios";
headerRow.appendChild(dayHeader);
headerRow.appendChild(horarioHeader);
table.appendChild(headerRow);
days.forEach(day => {
const row = document.createElement("tr");
const dayCell = document.createElement("td");
dayCell.textContent = day.charAt(0).toUpperCase() + day.slice(1);
const horarioCell = document.createElement("td");
const input = document.createElement("input");
input.type = "text";
input.dataset.day = day;
input.value = horarios[day] || "";
horarioCell.appendChild(input);
row.appendChild(dayCell);
row.appendChild(horarioCell);
table.appendChild(row);
});
tableContainer.appendChild(table);
// Actualizar el campo oculto con el JSON
table.addEventListener("input", () => {
const updatedHorarios = {};
table.querySelectorAll("input").forEach(input => {
updatedHorarios[input.dataset.day] = input.value.trim();
});
document.querySelector("#_horario_disponible").value = JSON.stringify(updatedHorarios);
});
};
createTable();
});
</script>';
});
// Guardar datos del producto
add_action('woocommerce_process_product_meta', function($post_id) {
$es_reservable = isset($_POST['_es_reservable']) ? 'yes' : 'no';
update_post_meta($post_id, '_es_reservable', $es_reservable);
if (isset($_POST['_horario_disponible'])) {
update_post_meta($post_id, '_horario_disponible', sanitize_text_field($_POST['_horario_disponible']));
}
if (isset($_POST['_dias_festivos'])) {
update_post_meta($post_id, '_dias_festivos', sanitize_text_field($_POST['_dias_festivos']));
}
if (isset($_POST['_duracion_bloque_reserva'])) {
update_post_meta($post_id, '_duracion_bloque_reserva', sanitize_text_field($_POST['_duracion_bloque_reserva']));
}
});
add_filter('woocommerce_before_add_to_cart_button', function() {
global $product;
if ($product->get_meta('_es_reservable') === 'yes') {
$horarios = json_decode($product->get_meta('_horario_disponible'), true);
$festivos = $product->get_meta('_dias_festivos');
$reservas_existentes = obtener_reservas($product->get_id());
$festivos_array = $festivos ? explode(',', $festivos) : [];
$festivos_array = array_map(function($fecha) {
$date = DateTime::createFromFormat('Y-m-d', trim($fecha));
return $date ? $date->format('Y-m-d') : null;
}, $festivos_array);
$festivos_array = array_filter($festivos_array); // Eliminar nulos
$duracion_bloque = $product->get_meta('_duracion_bloque_reserva') ?: 30; // Por defecto 30 minutos
echo '<div class="modern-reservation-form">';
echo '<label for="dia_reserva">Selecciona tu día:</label>';
echo '<select name="dia_reserva" class="modern-select" required>';
$fecha_actual = new DateTime();
$fecha_actual->modify('+1 day'); // Solo a partir del día siguiente
for ($i = 0; $i < 90; $i++) {
$dia_semana = strtolower($fecha_actual->format('l'));
$dia_semana_es = convertir_dia_a_espanol($dia_semana);
$fecha_actual_str = $fecha_actual->format('Y-m-d');
// Verificar si el día tiene horarios, no está en festivos y no tiene todas las horas reservadas
if (
isset($horarios[$dia_semana_es]) &&
!empty($horarios[$dia_semana_es]) &&
!in_array($fecha_actual_str, $festivos_array) &&
!todas_horas_reservadas($fecha_actual_str, $horarios[$dia_semana_es], $reservas_existentes)
) {
echo '<option value="' . esc_attr($fecha_actual_str) . '">' . esc_html($fecha_actual->format('d/m/Y')) . ' (' . ucfirst($dia_semana_es) . ')</option>';
}
$fecha_actual->modify('+1 day');
}
echo '</select>';
echo '<label for="hora_reserva">Selecciona tu hora:</label>';
echo '<select name="hora_reserva" class="modern-select" required></select>';
echo '</div>';
echo '<style>
.modern-reservation-form {
display: flex;
flex-direction: column;
gap: 1rem;
margin: 1.5rem 0;
}
.modern-reservation-form label {
font-weight: bold;
font-size: 1rem;
color: #333;
}
.modern-select {
font-size: 1rem;
padding: 0.75rem;
border: 2px solid #ddd;
border-radius: 5px;
background-color: #fff;
transition: all 0.3s ease;
color: #333;
}
.modern-select:focus {
border-color: #007bff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.5);
outline: none;
}
.modern-select:hover {
border-color: #0056b3;
}
</style>';
echo '<script>
document.addEventListener("DOMContentLoaded", function() {
const horarios = ' . json_encode($horarios) . ';
const reservas = ' . json_encode($reservas_existentes) . ';
const duracionBloque = ' . $duracion_bloque . ';
const selectDia = document.querySelector("select[name=dia_reserva]");
const selectHora = document.querySelector("select[name=hora_reserva]");
selectDia.addEventListener("change", function() {
const diaSeleccionado = this.value; // Valor en formato Y-m-d
const diaSemana = new Date(diaSeleccionado).toLocaleDateString("es-ES", { weekday: "long" }).toLowerCase();
selectHora.innerHTML = ""; // Limpiar las opciones previas
if (horarios[diaSemana]) {
horarios[diaSemana].split(",").forEach(rango => {
const [inicio, fin] = rango.split("-");
let hora = new Date("1970-01-01T" + inicio + ":00");
const finHora = new Date("1970-01-01T" + fin + ":00");
while (hora < finHora) {
const horaTexto = hora.toTimeString().slice(0, 5);
const valor = diaSeleccionado + " " + horaTexto;
// Comprobar si la hora está reservada
if (!reservas.includes(valor)) {
const option = document.createElement("option");
option.value = horaTexto;
option.textContent = horaTexto;
selectHora.appendChild(option);
}
hora.setMinutes(hora.getMinutes() + parseInt(duracionBloque));
}
});
}
});
// Disparar el evento de cambio para el primer día seleccionado
if (selectDia.value) {
selectDia.dispatchEvent(new Event("change"));
}
});
</script>';
}
});
// Función para obtener reservas existentes
function obtener_reservas($product_id) {
$reservas = [];
$orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);
foreach ($orders as $order) {
foreach ($order->get_items() as $item) {
if ($item->get_product_id() == $product_id) {
$dia = $item->get_meta('Día Reservado');
$hora = $item->get_meta('Hora Reservada');
if ($dia && $hora) {
$reservas[] = "$dia $hora";
}
}
}
}
return $reservas;
}
// Verificar si todas las horas de un día están reservadas
function todas_horas_reservadas($fecha, $horarios_dia, $reservas) {
$horas_reservadas = array_filter($reservas, function($reserva) use ($fecha) {
return strpos($reserva, $fecha) === 0;
});
$total_horas = 0;
foreach (explode(',', $horarios_dia) as $rango) {
[$inicio, $fin] = explode('-', $rango);
$inicio_hora = new DateTime("1970-01-01T$inicio:00");
$fin_hora = new DateTime("1970-01-01T$fin:00");
while ($inicio_hora < $fin_hora) {
$total_horas++;
$inicio_hora->modify('+30 minutes');
}
}
return count($horas_reservadas) >= $total_horas;
}
// Convertir día al español
function convertir_dia_a_espanol($dia) {
$dias = [
'monday' => 'lunes',
'tuesday' => 'martes',
'wednesday' => 'miércoles',
'thursday' => 'jueves',
'friday' => 'viernes',
'saturday' => 'sábado',
'sunday' => 'domingo'
];
return $dias[$dia] ?? $dia;
}
// Validar antes de añadir al carrito
add_filter('woocommerce_add_to_cart_validation', function($passed, $product_id) {
if (isset($_POST['dia_reserva'], $_POST['hora_reserva'])) {
$reserva = sanitize_text_field($_POST['dia_reserva']) . ' ' . sanitize_text_field($_POST['hora_reserva']);
if (in_array($reserva, obtener_reservas($product_id))) {
wc_add_notice('La fecha y hora seleccionadas ya están reservadas.', 'error');
return false;
}
}
return $passed;
}, 10, 2);
// Guardar reserva en el carrito
add_filter('woocommerce_add_cart_item_data', function($cart_item_data, $product_id, $variation_id) {
if (isset($_POST['dia_reserva'], $_POST['hora_reserva'])) {
$cart_item_data['dia_reserva'] = sanitize_text_field($_POST['dia_reserva']);
$cart_item_data['hora_reserva'] = sanitize_text_field($_POST['hora_reserva']);
}
return $cart_item_data;
}, 10, 3);
// Mostrar reserva en el carrito y la página de pago
add_filter('woocommerce_get_item_data', function($item_data, $cart_item) {
if (isset($cart_item['dia_reserva'], $cart_item['hora_reserva'])) {
$item_data[] = [
'key' => __('Día Reservado', 'woocommerce'),
'value' => $cart_item['dia_reserva']
];
$item_data[] = [
'key' => __('Hora Reservada', 'woocommerce'),
'value' => $cart_item['hora_reserva']
];
}
return $item_data;
}, 10, 2);
// Guardar reserva en el pedido
add_action('woocommerce_checkout_create_order_line_item', function($item, $cart_item_key, $values) {
if (isset($values['dia_reserva'], $values['hora_reserva'])) {
$item->add_meta_data('Día Reservado', $values['dia_reserva']);
$item->add_meta_data('Hora Reservada', $values['hora_reserva']);
$item->add_meta_data('Estado de la Reserva', 'Activa');
}
}, 10, 3);
// Página de administración para gestionar reservas
add_action('admin_menu', function() {
add_menu_page(
__('Reservas', 'woocommerce'),
__('Reservas', 'woocommerce'),
'manage_woocommerce',
'reservas',
'mostrar_pagina_reservas',
'dashicons-calendar-alt',
56
);
});
function mostrar_pagina_reservas() {
echo '<div class="wrap">';
echo '<h1>' . __('Gestión de Reservas', 'woocommerce') . '</h1>';
if (isset($_POST['cancelar_reserva'])) {
procesar_cancelacion_reserva($_POST['reserva_dia'], $_POST['reserva_hora'], $_POST['producto']);
echo '<div class="updated"><p>' . __('Reserva cancelada correctamente.', 'woocommerce') . '</p></div>';
}
if (isset($_POST['eliminar_reserva'])) {
eliminar_reserva($_POST['reserva_dia'], $_POST['reserva_hora'], $_POST['producto']);
echo '<div class="updated"><p>' . __('Reserva eliminada correctamente.', 'woocommerce') . '</p></div>';
}
$reservas = obtener_reservas_admin();
if (!empty($reservas)) {
echo '<table id="reservas-table" class="wp-list-table widefat fixed striped">';
echo '<thead><tr>';
echo '<th>' . __('Cliente', 'woocommerce') . '</th>';
echo '<th>' . __('Email', 'woocommerce') . '</th>';
echo '<th>' . __('Teléfono', 'woocommerce') . '</th>';
echo '<th>' . __('Producto', 'woocommerce') . '</th>';
echo '<th>' . __('Fecha Reservada', 'woocommerce') . '</th>';
echo '<th>' . __('Hora Reservada', 'woocommerce') . '</th>';
echo '<th>' . __('Estado', 'woocommerce') . '</th>';
echo '<th>' . __('Acciones', 'woocommerce') . '</th>';
echo '</tr></thead><tbody>';
foreach ($reservas as $reserva) {
echo '<tr>';
echo '<td><a href="mailto:' . esc_html($reserva['email']) . '">' . esc_html($reserva['cliente']) . '</a></td>';
echo '<td>' . esc_html($reserva['email']) . '</td>';
echo '<td>' . esc_html($reserva['telefono']) . '</td>';
echo '<td>' . esc_html($reserva['producto']) . '</td>';
echo '<td>' . esc_html($reserva['fecha']) . '</td>';
echo '<td>' . esc_html($reserva['hora']) . '</td>';
$estado_color = $reserva['estado'] === 'Cancelada' ? 'red' : 'green';
echo '<td style="color:' . $estado_color . ';">' . esc_html($reserva['estado']) . '</td>';
echo '<td>';
if ($reserva['estado'] !== 'Cancelada') {
echo '<form method="post" style="display:inline-block;">';
echo '<input type="hidden" name="reserva_dia" value="' . esc_attr($reserva['fecha']) . '">';
echo '<input type="hidden" name="reserva_hora" value="' . esc_attr($reserva['hora']) . '">';
echo '<input type="hidden" name="producto" value="' . esc_attr($reserva['producto']) . '">';
echo '<button type="submit" name="cancelar_reserva" class="button-link-delete">' . __('Cancelar', 'woocommerce') . '</button>';
echo '</form>';
}
if ($reserva['estado'] === 'Cancelada') {
echo '<form method="post" style="display:inline-block;">';
echo '<input type="hidden" name="reserva_dia" value="' . esc_attr($reserva['fecha']) . '">';
echo '<input type="hidden" name="reserva_hora" value="' . esc_attr($reserva['hora']) . '">';
echo '<input type="hidden" name="producto" value="' . esc_attr($reserva['producto']) . '">';
echo '<button type="submit" name="eliminar_reserva" class="button-link-delete">' . __('Eliminar', 'woocommerce') . '</button>';
echo '</form>';
}
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
} else {
echo '<p>' . __('No hay reservas disponibles.', 'woocommerce') . '</p>';
}
echo '</div>';
}
function obtener_reservas_admin() {
$reservas = [];
$orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);
foreach ($orders as $order) {
foreach ($order->get_items() as $item) {
$fecha = $item->get_meta('Día Reservado');
$hora = $item->get_meta('Hora Reservada');
$estado = $item->get_meta('Estado de la Reserva') ?: 'Activa';
if ($fecha && $hora) {
$reservas[] = [
'cliente' => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
'email' => $order->get_billing_email(),
'telefono' => $order->get_billing_phone(),
'producto' => $item->get_name(),
'fecha' => $fecha,
'hora' => $hora,
'estado' => $estado
];
}
}
}
return $reservas;
}
// Funciones adicionales al código previo
function eliminar_reserva($dia, $hora, $producto) {
$orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);
foreach ($orders as $order) {
foreach ($order->get_items() as $item_id => $item) {
if ($item->get_meta('Día Reservado') === $dia && $item->get_meta('Hora Reservada') === $hora && $item->get_name() === $producto) {
wc_delete_order_item_meta($item_id, 'Día Reservado');
wc_delete_order_item_meta($item_id, 'Hora Reservada');
wc_delete_order_item_meta($item_id, 'Estado de la Reserva');
break 2; // Salir de ambos bucles
}
}
}
}
function procesar_cancelacion_reserva($dia, $hora, $producto) {
$orders = wc_get_orders(['limit' => -1, 'status' => ['wc-completed', 'wc-processing', 'wc-on-hold', 'wc-pending']]);
foreach ($orders as $order) {
foreach ($order->get_items() as $item_id => $item) {
if ($item->get_meta('Día Reservado') === $dia && $item->get_meta('Hora Reservada') === $hora && $item->get_name() === $producto) {
wc_update_order_item_meta($item_id, 'Estado de la Reserva', 'Cancelada');
break 2; // Salir de ambos bucles
}
}
}
}
// Encolar DataTables solo en la página de reservas
add_action('admin_enqueue_scripts', function($hook) {
if ($hook === 'toplevel_page_reservas') { // Cambia 'toplevel_page_reservas' según el slug de tu página
// Incluir estilos y scripts de DataTables desde el CDN
wp_enqueue_script('datatables-js', 'https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js', ['jquery'], null, true);
wp_enqueue_style('datatables-css', 'https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css');
// Inicializar DataTables en la tabla de reservas
wp_add_inline_script('datatables-js', '
jQuery(document).ready(function($) {
$("#reservas-table").DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.13.4/i18n/es-ES.json"
},
"order": [[4, "asc"]], // Ordenar por la columna "Fecha Reservada" (índice 4)
"pageLength": 10, // Mostrar 10 filas por página
"responsive": true // Hacer la tabla responsiva
});
});
');
}
});
// Reemplazar el botón de "Añadir al carrito" en la tienda para productos reservables
add_filter('woocommerce_loop_add_to_cart_link', function($link, $product) {
if ($product->get_meta('_es_reservable') === 'yes') {
$product_url = get_permalink($product->get_id());
$button_text = __('Elegir Fecha y Hora', 'woocommerce');
return '<a href="' . esc_url($product_url) . '" class="button choose-date-time">' . esc_html($button_text) . '</a>';
}
return $link;
}, 10, 2);
Lenguaje del código: PHP (php)
En conclusión, este sistema de reservas personalizadas para WooCommerce proporciona una solución robusta y flexible para gestionar productos o servicios que requieren selección de horarios específicos. Desde la configuración de horarios y días festivos hasta la gestión avanzada de reservas desde el panel administrativo, esta integración permite a los negocios ofrecer una experiencia fluida tanto para los clientes como para los administradores.
Gracias a funcionalidades como la validación de reservas existentes, la personalización de bloques de tiempo y un diseño intuitivo, este código mejora la interacción con los usuarios finales y optimiza los procesos de reserva. Es una herramienta ideal para negocios que buscan simplificar la administración de sus servicios y garantizar una experiencia de usuario profesional y personalizada.
Contáctame por WhatsApp para servicios de desarrollo personalizado, ajustes en tu tienda online o cualquier solución a medida que necesites.
➡️Haz clic aquí para contactarme por WhatsApp
Nos vemos en el siguiente tutorial.
Un saludo.




Deja tus opiniones o dudas en los comentarios.
Estoy hecha un lio. Hay tantas opciones...
Bookly, Amelia ahora me encuentro este código. Me ayudas a elegir para una tienda de estética? Te dejé otro comentario en el post de Bookly. Muchas gracias.
Buenas, como te dije en el otro comentario lo mejor que puedes hacer es probarlo todo, ver la interfaz, hacer pruebas y te quedas con el que se adapte más a lo que necesitas. Primero fíjate en la funcionalidad antes que el diseño. El diseño es más fácil cambiarlo.
En cuanto a este código, es algo más simple. Pensado para una única persona que ofrece sus servicios online sin mucha complicación. Saludos.
# Hola! El código no me funciona correctamente.
La previsualización de los días sale cortada y no permite elegir una hora... No sé si estoy haciendo algo mal, pero no he modificado nada del código, solo copiar y pegar. ¿Hay alguna solución?
Muchas gracias!!
Hola, podría ser tu tema y que tengas que retocar algo con CSS. El código funciona perfectamente en esta web de pruebas con el tema Blocksy.
https://codigos.fransalas-proyectos.com/product/producto-reservable/