Registro de Recepción
:root {
–bg: #f5f0e8;
–surface: #fffdf8;
–border: #d6cfc0;
–accent: #1a3a2a;
–accent2: #c8a96e;
–text: #1c1c1c;
–text-muted: #7a7060;
–danger: #b84040;
–row-hover: #f0ead8;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: ‘DM Mono’, monospace;
background: var(–bg);
color: var(–text);
min-height: 100vh;
padding: 2rem;
}
header {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 2.5rem;
padding-bottom: 1.5rem;
border-bottom: 2px solid var(–accent);
}
.header-left h1 {
font-family: ‘DM Serif Display’, serif;
font-size: 2.4rem;
color: var(–accent);
letter-spacing: -0.5px;
line-height: 1;
}
.header-left p {
font-size: 0.75rem;
color: var(–text-muted);
margin-top: 0.4rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.date-badge {
background: var(–accent);
color: #fff;
padding: 0.5rem 1.2rem;
font-size: 0.85rem;
letter-spacing: 0.06em;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.1rem;
}
.date-badge span:first-child {
font-size: 1.4rem;
font-family: ‘DM Serif Display’, serif;
line-height: 1;
}
/* Formulario de nueva entrada */
.form-card {
background: var(–surface);
border: 1.5px solid var(–border);
padding: 1.5rem 2rem;
margin-bottom: 2rem;
}
.form-title {
font-size: 0.7rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(–accent2);
margin-bottom: 1rem;
font-weight: 500;
}
.form-row {
display: grid;
grid-template-columns: 2fr 1fr 1fr 1fr 1fr auto;
gap: 0.75rem;
align-items: end;
}
.field {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.field label {
font-size: 0.65rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(–text-muted);
}
.field input {
font-family: ‘DM Mono’, monospace;
font-size: 0.9rem;
padding: 0.5rem 0.7rem;
border: 1.5px solid var(–border);
background: var(–bg);
color: var(–text);
outline: none;
transition: border-color 0.15s;
width: 100%;
}
.field input:focus {
border-color: var(–accent);
background: #fff;
}
.btn-add {
font-family: ‘DM Mono’, monospace;
font-size: 0.85rem;
padding: 0.52rem 1.4rem;
background: var(–accent);
color: #fff;
border: none;
cursor: pointer;
letter-spacing: 0.05em;
transition: background 0.15s;
white-space: nowrap;
height: fit-content;
}
.btn-add:hover { background: #254d38; }
/* Tabla */
.table-wrap {
background: var(–surface);
border: 1.5px solid var(–border);
overflow-x: auto;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.9rem 1.5rem;
border-bottom: 1.5px solid var(–border);
}
.table-header span {
font-size: 0.7rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(–accent2);
font-weight: 500;
}
.entry-count {
font-size: 0.75rem;
color: var(–text-muted);
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
thead tr {
background: var(–accent);
color: #fff;
}
thead th {
padding: 0.7rem 1rem;
text-align: left;
font-size: 0.65rem;
letter-spacing: 0.12em;
text-transform: uppercase;
font-weight: 500;
}
tbody tr {
border-bottom: 1px solid var(–border);
transition: background 0.1s;
}
tbody tr:hover { background: var(–row-hover); }
tbody tr:last-child { border-bottom: none; }
tbody td {
padding: 0.65rem 1rem;
color: var(–text);
}
.td-hab {
font-family: ‘DM Serif Display’, serif;
font-size: 1.1rem;
color: var(–accent);
}
.td-hora {
color: var(–text-muted);
}
.btn-del {
background: none;
border: 1px solid var(–border);
color: var(–text-muted);
font-size: 0.7rem;
font-family: ‘DM Mono’, monospace;
padding: 0.2rem 0.6rem;
cursor: pointer;
transition: all 0.15s;
letter-spacing: 0.05em;
}
.btn-del:hover {
border-color: var(–danger);
color: var(–danger);
}
/* Fila lista */
tr.lista {
opacity: 0.45;
background: #e8e2d4 !important;
}
tr.lista td {
text-decoration: line-through;
color: var(–text-muted);
}
tr.lista .td-hab {
color: var(–text-muted);
}
/* Checkbox hab lista */
.check-lista {
width: 1.2rem;
height: 1.2rem;
accent-color: var(–accent);
cursor: pointer;
}
.empty-state {
text-align: center;
padding: 3rem;
color: var(–text-muted);
font-size: 0.8rem;
letter-spacing: 0.08em;
}
/* Acciones inferiores */
.actions {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
justify-content: flex-end;
}
.btn-secondary {
font-family: ‘DM Mono’, monospace;
font-size: 0.8rem;
padding: 0.5rem 1.2rem;
background: transparent;
border: 1.5px solid var(–accent);
color: var(–accent);
cursor: pointer;
letter-spacing: 0.05em;
transition: all 0.15s;
}
.btn-secondary:hover {
background: var(–accent);
color: #fff;
}
.btn-gold {
border-color: var(–accent2);
color: var(–accent2);
}
.btn-gold:hover {
background: var(–accent2);
color: #fff;
}
@media (max-width: 900px) {
.form-row {
grid-template-columns: 1fr 1fr;
}
body { padding: 1rem; }
}
/* ── Modal email ───────────────────────────────────────────── */
.modal-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(10,20,15,0.55);
z-index: 100;
align-items: center;
justify-content: center;
}
.modal-overlay.open { display: flex; }
.modal {
background: var(–surface);
border: 1.5px solid var(–border);
width: 100%;
max-width: 540px;
margin: 1rem;
box-shadow: 0 8px 40px rgba(0,0,0,0.18);
animation: slideUp 0.2s ease;
}
@keyframes slideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal-head {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
border-bottom: 1.5px solid var(–border);
background: var(–accent);
color: #fff;
}
.modal-head span {
font-size: 0.7rem;
letter-spacing: 0.15em;
text-transform: uppercase;
}
.modal-close {
background: none;
border: none;
color: #fff;
font-size: 1.3rem;
cursor: pointer;
line-height: 1;
padding: 0 0.2rem;
}
.modal-body {
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.modal-field {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.modal-field label {
font-size: 0.65rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(–text-muted);
}
.modal-field input,
.modal-field textarea {
font-family: ‘DM Mono’, monospace;
font-size: 0.85rem;
padding: 0.55rem 0.8rem;
border: 1.5px solid var(–border);
background: var(–bg);
color: var(–text);
outline: none;
transition: border-color 0.15s;
width: 100%;
resize: vertical;
}
.modal-field input:focus,
.modal-field textarea:focus {
border-color: var(–accent);
background: #fff;
}
.modal-preview {
background: var(–bg);
border: 1px dashed var(–border);
padding: 1rem;
font-size: 0.8rem;
line-height: 1.7;
color: var(–text);
white-space: pre-wrap;
}
.modal-preview-label {
font-size: 0.6rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(–accent2);
margin-bottom: 0.4rem;
}
.modal-foot {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
padding: 1rem 1.5rem;
border-top: 1.5px solid var(–border);
}
.btn-email {
font-family: ‘DM Mono’, monospace;
font-size: 0.8rem;
padding: 0.5rem 1.4rem;
background: var(–accent);
color: #fff;
border: none;
cursor: pointer;
letter-spacing: 0.05em;
transition: background 0.15s;
}
.btn-email:hover { background: #254d38; }
.btn-email-row {
background: none;
border: 1px solid var(–accent2);
color: var(–accent2);
font-size: 0.7rem;
font-family: ‘DM Mono’, monospace;
padding: 0.2rem 0.7rem;
cursor: pointer;
transition: all 0.15s;
letter-spacing: 0.04em;
white-space: nowrap;
}
.btn-email-row:hover {
background: var(–accent2);
color: #fff;
}
| # |
Apellido |
Hab. |
Hora llegada |
Timing HK |
Timing RS |
Hab. lista |
Email |
|
| Sin registros aún. Añade la primera entrada. |
✉ Envoyer un email au client
Adresse email du client
Objet
// ── Fecha ──────────────────────────────────────────────────────
const now = new Date();
const dias = [‘Domingo’,’Lunes’,’Martes’,’Miércoles’,’Jueves’,’Viernes’,’Sábado’];
const meses = [‘Enero’,’Febrero’,’Marzo’,’Abril’,’Mayo’,’Junio’,’Julio’,’Agosto’,’Septiembre’,’Octubre’,’Noviembre’,’Diciembre’];
document.getElementById(‘dia-num’).textContent = `${dias[now.getDay()]} ${now.getDate()}`;
document.getElementById(‘mes-ano’).textContent = `${meses[now.getMonth()]} ${now.getFullYear()}`;
const HOY = now.toISOString().slice(0, 10);
const STORAGE_KEY = `recepcion_${HOY}`;
// ── Datos ──────────────────────────────────────────────────────
let entries = [];
function load() {
const raw = localStorage.getItem(STORAGE_KEY);
entries = raw ? JSON.parse(raw) : [];
render();
}
function save() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(entries));
}
// ── Añadir ─────────────────────────────────────────────────────
function addEntry() {
const apellido = document.getElementById(‘inp-apellido’).value.trim();
const hab = document.getElementById(‘inp-hab’).value.trim();
const llegada = document.getElementById(‘inp-llegada’).value;
const hk = document.getElementById(‘inp-hk’).value;
const rs = document.getElementById(‘inp-rs’).value;
if (!apellido) { alert(‘Introduce el apellido del cliente.’); return; }
if (!/^\d{3}$/.test(hab)) { alert(‘La habitación debe tener exactamente 3 dígitos.’); return; }
entries.push({ apellido, hab, llegada, hk, rs, lista: false });
save();
render();
clearForm();
}
function clearForm() {
[‘inp-apellido’,’inp-hab’,’inp-llegada’,’inp-hk’,’inp-rs’].forEach(id => {
document.getElementById(id).value = ”;
});
document.getElementById(‘inp-apellido’).focus();
}
// ── Eliminar ───────────────────────────────────────────────────
function deleteEntry(i) {
if (!confirm(‘¿Eliminar este registro?’)) return;
entries.splice(i, 1);
save();
render();
}
// ── Toggle hab. lista ──────────────────────────────────────────
function toggleLista(origIndex, checked) {
entries[origIndex].lista = checked;
save();
render();
}
// ── Renderizar ─────────────────────────────────────────────────
function render() {
const tbody = document.getElementById(‘tabla-body’);
document.getElementById(‘entry-count’).textContent =
`${entries.length} registro${entries.length !== 1 ? ‘s’ : ”}`;
if (entries.length === 0) {
tbody.innerHTML = `
| Sin registros aún. Añade la primera entrada. |
`;
return;
}
// Pendientes primero, listos al final
const sorted = entries
.map((e, i) => ({ …e, _orig: i }))
.sort((a, b) => (a.lista === b.lista ? 0 : a.lista ? 1 : -1));
tbody.innerHTML = sorted.map((e, i) => `
| ${i + 1} |
${e.apellido} |
${e.hab} |
${e.llegada || ‘—’} |
${e.hk || ‘—’} |
${e.rs || ‘—’} |
|
|
|
`).join(”);
}
// ── Limpiar día ────────────────────────────────────────────────
function clearDay() {
if (!confirm(‘¿Borrar todos los registros de hoy?’)) return;
entries = [];
save();
render();
}
// ── Exportar CSV ───────────────────────────────────────────────
function exportCSV() {
if (entries.length === 0) { alert(‘No hay registros para exportar.’); return; }
const header = ‘Fecha,Apellido,Habitación,Hora llegada,Timing HK,Timing RS,Hab. lista’;
const rows = entries.map(e =>
`${HOY},”${e.apellido}”,${e.hab},${e.llegada || ”},${e.hk || ”},${e.rs || ”},${e.lista ? ‘Sí’ : ‘No’}`
);
const csv = [header, …rows].join(‘\n’);
const blob = new Blob([csv], { type: ‘text/csv;charset=utf-8;’ });
const url = URL.createObjectURL(blob);
const a = document.createElement(‘a’);
a.href = url;
a.download = `recepcion_${HOY}.csv`;
a.click();
URL.revokeObjectURL(url);
}
// ── Enter para añadir ──────────────────────────────────────────
document.addEventListener(‘keydown’, e => {
if (e.key === ‘Enter’) addEntry();
});
// ── Modal email ────────────────────────────────────────────────
let modalEntry = null;
function openModal(i) {
modalEntry = entries[i];
const meses_fr = [‘janvier’,’février’,’mars’,’avril’,’mai’,’juin’,’juillet’,’août’,’septembre’,’octobre’,’novembre’,’décembre’];
const dateStr = `${now.getDate()} ${meses_fr[now.getMonth()]} ${now.getFullYear()}`;
const asunto = `Informations — Chambre ${modalEntry.hab}`;
const mensaje =
`Madame, Monsieur ${modalEntry.apellido},
Nous avons le plaisir de vous accueillir et vous communiquons ci-dessous les informations relatives à votre chambre :
• Chambre n° : ${modalEntry.hab}
• Date d’arrivée : ${dateStr}${modalEntry.llegada ? `\n • Heure d’arrivée : ${modalEntry.llegada}` : ”}${modalEntry.hk ? `\n • Mise à disposition (HK) : ${modalEntry.hk}` : ”}${modalEntry.rs ? `\n • Room Service disponible à partir de : ${modalEntry.rs}` : ”}
N’hésitez pas à contacter la réception pour toute demande.
Nous vous souhaitons un agréable séjour.
L’équipe de la Réception`;
document.getElementById(‘modal-asunto’).value = asunto;
document.getElementById(‘modal-preview’).textContent = mensaje;
document.getElementById(‘modal-dest’).value = ”;
document.getElementById(‘modal-overlay’).classList.add(‘open’);
setTimeout(() => document.getElementById(‘modal-dest’).focus(), 100);
}
function closeModal() {
document.getElementById(‘modal-overlay’).classList.remove(‘open’);
modalEntry = null;
}
function sendEmail() {
const dest = document.getElementById(‘modal-dest’).value.trim();
const asunto = document.getElementById(‘modal-asunto’).value.trim();
const cuerpo = document.getElementById(‘modal-preview’).textContent;
if (!dest) { alert(‘Veuillez saisir l\’adresse email du client.’); return; }
const mailto = `mailto:${dest}?subject=${encodeURIComponent(asunto)}&body=${encodeURIComponent(cuerpo)}`;
window.location.href = mailto;
closeModal();
}
// Cerrar modal con Escape
document.addEventListener(‘keydown’, e => {
if (e.key === ‘Escape’) closeModal();
});
// ── Init ───────────────────────────────────────────────────────
load();
Compartir | partager | share