// Evidence Dossier
Cabañas Las Palmas
Management system for a cabin resort: a public inquiry site and a private panel for guests, reservations, payments, and reports. In active production; known integrity gaps limit stronger claims.
- Who it serves
Two administrators register guests and manage the resort. Visitors view cabins and inquire via WhatsApp; they do not book or pay through the public site.
- My responsibility
I designed and developed the site, dashboard, APIs, database, and deployment end to end as sole developer, with deployment in late June.
- Current status
In active production and used by two administrators since late June. This review examined code and local execution, not the production server.
- What it enables
Combines cabins, guests, bookings, deposits, expenses, and reports. There is no verified measurement of time saved or guarantee of zero overbooking.
Context
Cabañas Las Palmas combines a public showcase with a private reception tool. Two administrators use it daily to record guests. The public page shows cabins, a gallery, and WhatsApp contact; it does not publish computed availability or offer booking checkout.
Problem
With a small physical inventory, reception must connect each stay to a cabin, guest, dates, and payments. A visual list has limited value if creation, status changes, or payment handling can leave contradictory records. The system aims to centralize these tasks; complete elimination of overbooking or payment mistakes has not been proven.
My role
I designed and built the entire project: public experience, dashboard, server logic, persistence, and deployment as end-to-end solo development, reflected across repository architecture and components.
Constraints
A single-server architecture with SQLite avoids operating a separate database, but requires durable storage for the database, images, and backups plus a tested restore policy. Stay dates are calendar days; checkout and check-in on the same date must be compatible. Guest and payment information stays out of this case study and its tests.
Solution
The public site presents active cabins, photos, amenities, and a WhatsApp inquiry link. In the panel, an authenticated staff member registers guests, creates stays for a cabin and date range, reviews occupancy in tables, records payments and extras, and obtains reports or exports. An email receipt after booking is optional: delivery failure does not undo a saved booking. There is no payment gateway or visitor self-booking flow.
Architecture & diagrams
A Next.js 16 application with React 19 separates public presentation from private views. Route Handlers validate inputs and permissions, while better-sqlite3 stores cabins, guests, bookings, and payments in SQLite with WAL and foreign keys. Booking creation and full edits use BEGIN IMMEDIATE to check overlap and insert within a serialized write. Public content reads visible cabins, not the occupancy calendar. ExcelJS creates authorized exports; PDFKit and Nodemailer serve receipt and notification flows when configured.
Read the diagram and its limits
1. Workflow and actors: The visitor browses cabins and rates on the public showcase (Next.js App Router) and initiates booking inquiries via WhatsApp. The operator receives inquiries, manually verifies cabin availability, and enters the private management panel using a JWT HttpOnly cookie session.
2. Components and connections: Solid arrows show direct data flow (public browsing, authentication, bookings, add-ons, and payment records). Storage arrows represent immediate transactional writes to SQLite (better-sqlite3 in WAL mode) and filesystem storage for cabin photo galleries and backups (Sharp).
3. Operational outputs: PDF vouchers (PDFKit) and occupancy/revenue reports (ExcelJS) are generated dynamically from SQLite data upon operator request.
4. Verification status: The system operates in live production with actual guests and manual payments; local automated tests verified HTTP endpoints, database schema integrity, PDF generation, and Excel exports without connecting to external payment gateways.
5. Boundaries and known defect: Reactivating cancelled bookings bypasses date collision checks, allowing overlapping reservations (defect reproduced and documented in validation). Optional email delivery via Nodemailer was not verified against a live external SMTP server in local tests.
Implementation map verified in live production and local environment. Bookings do not use automated payment gateways; availability coordination is handled manually via WhatsApp.
Critical booking flow
1. Reception signs in and opens reservations; the browser loads cabins, guests, and bookings from private handlers.
2. Staff select the guest, unit, and dates. The server validates identifiers, dates, and rate.
3. An immediate transaction checks for the guest and active cabin, searches for overlapping active stays, and inserts a booking with nights and total if none exists. An overlap returns a conflict.
4. The response appears in the list. Reception can record a deposit; its handler recalculates the balance in another transaction and stores the payment.
5. A booking notification is attempted after the transaction is confirmed and may be skipped or fail without deleting the booking. This journey has no external charge or automatic visitor acceptance.
Concurrency and data integrity
The main creation route serializes the availability read and insert with BEGIN IMMEDIATE. It uses semi-open date intervals, so checkout and check-in on the same day do not conflict. A key limitation remains: a status-only update skips that overlap check and can reactivate a conflicting stay. Payment routes also lack a stored operation identity to recognize a retry after a lost response; two valid deposits can be duplicated. Restore behavior and durable storage at launch need further verification. These paths rule out promises of zero overbooking, idempotency, or proven recovery.
Engineering decisions
SQLite with WAL and immediate transactions fits a single instance with modest write volume, provided the file remains on durable local storage and deployment is controlled. Stays use YYYY-MM-DD dates with an exclusive checkout boundary, allowing same-day turnover. Email receipts are attempted after persistence so an email provider cannot block the booking record. These decisions are visible in code; no formal ADRs were found in the source repository, so none are invented here.
Security and data protection
Private APIs verify JWTs, active sessions, and roles on the server; mutations check origin, size, and input shape. Business queries use parameters, and export requires a permission. Admin and owner currently share one organization's business data; there is no tenant isolation. A local September 22 audit also identified pending risks in historical secrets, dependencies, authentication, bookings, and recovery. Implemented safeguards do not amount to a security certification or prove the effective production setup.
Testing and verification scope
This case study was validated in an isolated environment containing the source code with Node.js 22.23.2, npm ci, and synthetic data. The original Vitest suite ran without changed assertions, together with lint, TypeScript, and a build. A local journey covered a fictional account, booking creation and reading, a deposit, and persistence; public navigation was inspected separately. Exact results, failures, and conditions appear in the table and internal record.
The project's historical v8 coverage includes only two helpers named in vitest.config.mjs. It does not measure the booking engine, handlers, UI, or production. A previous 20-request test exercised an in-memory simulator and cannot establish real SQLite contention.
| Check | Result | Scope |
|---|---|---|
| Original suite | 59/59 tests · 16 files | Vitest 4.1.11 on Node.js 22.23.2; isolated environment with original assertions. |
| Static checks and build | Lint, TypeScript, and build passed | ESLint, tsc --noEmit, and Next.js 16.3.3 build in the isolated copy. |
| HTTP journey | Core scenarios passed | Compiled server on loopback with synthetic account and database: access, create, read, deposit, overlap rejection, and XLSX. SMTP disabled. |
| Concurrent requests | 1 creation · 11 conflicts | 12 HTTP requests started together against one process and one local SQLite database. Does not prove multiple processes or sustained load. |
| Status change | Defect reproduced | Reactivating a canceled booking after creating another for overlapping dates left two active conflicting stays. |
| Coverage and production | Limited scope | Configured coverage measures two helpers only. The real server, SMTP, restore, and business metrics were not inspected. |
Deployment and operation
The repository prepares a standalone Next.js build and includes scripts for a Node process, proxy, and encrypted backups conditional on configured keys. The system is deployed in active production and used by two administrators. This review did not access the server or verify the proxy, TLS, permissions, file destinations, backup jobs, restore, or email delivery. Older deployment notes describe a release that lost cabin names and photos; later code separates data from release assets, but the effective server behavior still needs checking.
Outcome and operational impact
The tool lets reception record and review guests, stays, payments, and reports in one panel. Daily use by two administrators reflects real resort operations. No traceable measurement of avoided overbooking, booking duration, savings, or adoption is available, so none is presented as a result.
Evidence and limits
Implemented: public showcase with WhatsApp contact, private panel, booking and payment handlers, SQLite WAL, export, and optional notification. Locally checked: the specific synthetic-data scenarios in the table. Simulated: external services and any concurrency test using an in-memory engine. Live operational status: sole development, active production, and continuous use by two administrators. Pending: production configuration, real email, restore, business outcomes, and closure of integrity defects. The September 22 audit and this review's sanitized record remain internal.
Concurrency & overbooking evidence
The earlier 20-task sample tested only a class written for the portfolio. This review sent 12 HTTP requests together to the real handler and a local SQLite database: one booking was created and eleven conflicted. It used one process; neither multiple processes nor sustained load was measured. A status update reactivated an overlapping stay in the same environment.
Operational observations & live deployment
End-to-end solo development, currently in active production with two administrators. No server data or user testimonials were used. Operational metrics still lack verifiable provenance.
Software quality and UX
The public page shows real cabin units and photographs and offers WhatsApp contact; the dashboard brings reception tables and forms together. The hero screenshot depicts the public showcase, not an online booking engine. This dossier was reviewed on desktop and mobile, including keyboard and modal scrolling; that describes local review rather than product accessibility certification.
Code sample and engineering test
A small public illustration of the overlap predicate used on creation: it compares two calendar-day intervals for the same cabin and excludes canceled bookings. It does not implement transactions, payments, or the API.
// Isolated illustration of the reservation overlap predicate. |
// Inputs are validated YYYY-MM-DD calendar dates before this check. |
export function overlaps(existing, candidate) { |
return existing.cabinId === candidate.cabinId |
&& existing.status !== 'Cancelada' |
&& existing.checkIn < candidate.checkOut |
&& existing.checkOut > candidate.checkIn; |
} |
Isolated sample test
import assert from 'node:assert/strict'; |
|
const stay = { cabinId: 1, status: 'Reservada', checkIn: '2026-04-10', checkOut: '2026-04-14' }; |
assert.equal(overlaps(stay, { cabinId: 1, checkIn: '2026-04-12', checkOut: '2026-04-15' }), true); |
assert.equal(overlaps(stay, { cabinId: 1, checkIn: '2026-04-14', checkOut: '2026-04-17' }), false); |
assert.equal(overlaps(stay, { cabinId: 2, checkIn: '2026-04-12', checkOut: '2026-04-15' }), false); |
assert.equal(overlaps({ ...stay, status: 'Cancelada' }, { cabinId: 1, checkIn: '2026-04-12', checkOut: '2026-04-15' }), false); |
The sample and these four assertions were executed exactly as displayed as an isolated Node.js module. This proves the predicate for those cases; date validation and atomicity were examined separately in the real system.