A little time.A lot in sync.
PRODUCT DESIGN · WEB ENGINEERING · OPERATIONAL UX
2026

- Relationship
- Partner app
- Our role
- Product design & engineering
- Surfaces
- Customer & merchant web
- Product stage
- Locally validated foundation
THE TIME WE PROMISE PEOPLE
More than a square on a calendar.
The customer. The provider. The room. The people waiting. Everyone needs the same answer to a simple question: is this time available?

An appointment is a promise about time. Keeping that promise requires the customer’s booking page, the front desk, the service provider and every required resource to agree on what is actually available.
We built Bookalto’s working foundation around that agreement. A customer can choose a service and reserve a time from a mobile browser. A merchant can prepare a workspace, publish its booking page, add walk-ins, manage a waiting queue and move appointments through the working day. Those journeys share a PostgreSQL-backed scheduling engine.
This appointment booking platform case study explains the technical decisions behind that foundation: what we customized, how competing requests are resolved, how failed changes preserve existing appointments, and how the interface communicates the outcome of real operations.
Evidence boundary. This account reflects the application and local verification reviewed on 18 October 2026. Bookalto is a working foundation, with broader product scope still to deliver. The scenarios below illustrate conditions exercised by code and tests; they are not invented customer incidents. No production adoption, commercial uplift, delivery schedule or commit-level history is claimed.
An appointment is a promise.
The operational challenge
A service business coordinates several timelines at once. A customer selects an appointment online. Someone arrives without a reservation. A staff member needs to extend an existing visit. Another employee is looking at a screen that was refreshed a few seconds earlier.
Each action seems straightforward in isolation. Together, they create difficult questions. Does a customer reviewing their booking temporarily occupy the slot? Can an available provider be booked when the required room is occupied? What happens when a confirmation succeeds but the browser never receives the response? Does extending an appointment remove the buffer needed before the next one?
We treated these questions as core product behavior. The visual calendar is only one representation of that behavior. The underlying system must make coherent decisions even when requests arrive together, interfaces are stale or a browser loses connectivity.
The same care applies to day-to-day operations. “Waiting,” “confirmed,” “checked in,” “in service” and “completed” describe different facts. A useful workspace makes those differences visible and preserves them in the data model.
The brief we implemented
Our initial delivery connected two primary surfaces:
| Surface | User objective | Working foundation |
|---|---|---|
| Public booking | Choose a service and receive a dependable appointment | Service selection, server-authored times, temporary reservation, guest details, review, confirmation and booking management |
| Merchant workspace | Prepare and run a service day | Saved onboarding, publication, Today agenda, walk-ins, manual queue promotion, attendance actions, services and settings |
This scope gave us a complete path from setting up a business to fulfilling a booking. It also kept future features from obscuring the first operational slice. Advanced calendars, class seats, recurrence and automated waitlist matching remain separate work.
A few taps. A real commitment.
THE CUSTOMER JOURNEY / ACTUAL SCREENS
A service, with context.
Duration, price and the business’s policy appear before the customer chooses a time.
- 01Choose
- 02Reserve
- 03Review
- 04Confirm

What the team built
A merchant setup that survives interruption
The onboarding flow collects the business identity, branch details, timezone, opening hours, first service, initial provider and basic booking policy. Each Continue action saves a draft. Returning to the flow restores the saved values.
Final submission creates the workspace’s related records together: the business, first branch, initial provider, service, service-resource relationship and initial operating configuration. The setup does not leave a half-created business if a later insert fails. A repeated submission returns the existing workspace when one has already been created for the account.
This separation between a draft and a completed workspace is a small interface decision with a substantial backend consequence. The user can revise an unfinished setup without the public booking experience treating those values as a published operation.
Setup review in the running application. The studio, contact details and location are fictional.
A customer journey built for a phone
Customers open a business booking link, select a service, choose a server-returned start time, complete the guest details flow and review the appointment before confirming. They do not need to install an app or create a mandatory Bookalto account.
Selecting a time starts a short reservation. The interface displays the remaining time, the service duration, the branch timezone, the price and the current change policy. The confirmation screen appears after the booking operation succeeds.
The current implementation uses a five-minute hold by default. That is a foundation setting, subject to future product decisions and real delivery measurements. It should not be read as a universally approved checkout duration.
Customers can choose to remember their details for the same business. They can also view their bookings, cancel an eligible appointment and clear those remembered details. The customer reschedule API is present; a complete customer-facing time picker for that action is still pending.
A merchant journey that continues after confirmation
The merchant agenda shows the appointments for a branch-local date, alongside a waiting queue, resources and recent activity. Staff can search the day, filter appointments, open a detail sheet and perform the next operational action.
A timed walk-in acquires resources through the same scheduling logic as a public booking. A queue-only arrival remains a ticket until someone assigns an available time. Check-in, service start and completion are explicit actions. They are not inferred from the wall clock.
What happens when plans collide?
BEHIND THE APPOINTMENT / EXPLORE THE RULE
One available slot. Two requests.
The final decision happens inside the transaction, not in the browser.
The scheduling problems we designed for
The following examples are deliberately concrete. Names, times and business situations are illustrative. The mechanisms and referenced verification outcomes come from the implemented application.
1. One remaining slot, many competing requests
Scenario. Several customers see 10:00 as available. Before their screens refresh, they all try to reserve it. At the same time, a receptionist may be adding an arrival.
Availability shown in a browser is a recent observation. It cannot make a later booking authoritative. We therefore recheck the relevant conditions inside the write transaction before creating the hold or appointment.
The current implementation takes a branch-level row lock to establish a consistent order for booking-related writes. It checks the active service, required resources, hours, notice, booking horizon, closures and occupied intervals. PostgreSQL also applies an exclusion constraint to active exclusive-resource ranges.
The two mechanisms serve different purposes. The transaction coordinates application decisions. The database constraint independently rejects a conflicting active allocation. A screen refreshing slowly cannot make two overlapping exclusive allocations valid.
Evidence. The PostgreSQL suite issues 100 competing public hold attempts and verifies one winner followed by confirmation. A separate test issues 100 competing walk-ins and verifies one winner, then checks that the public path encounters the occupied time.
Tradeoff. Branch-level serialization is intentionally conservative. It simplifies the first implementation, but it can limit write throughput for a very busy branch. These contention checks demonstrate correctness under the tested conditions; they are not a production latency benchmark.
2. The provider is free, but another required resource is occupied
Scenario. A service requires both a provider and a room. The provider is available at 11:00, while the room is still reserved.
We model a service as requiring a set of exclusive resources. Every selected resource must be available. Selecting two providers does not currently mean “either provider”; it means the service requires both. Alternative resource selection would need a separate rule model.
Creating the booking inserts its resource allocations within one transaction. If any allocation fails, the transaction rolls back. The system cannot leave a provider reserved for an appointment that never acquired its room.
The occupied interval includes the service duration and its trailing buffer. A thirty-minute appointment with fifteen minutes of cleanup occupies forty-five minutes for scheduling purposes. This buffer affects availability even though the customer’s service duration remains thirty minutes.
Evidence. The integration suite checks multi-resource rollback and separately attempts an overlapping database allocation to verify the constraint itself. The current management UI can attach existing resources to services, but additional room/equipment creation and independent resource shift management are not complete product journeys.
3. A temporary reservation outlives its checkout
Scenario. A customer selects a time and leaves the browser open. Another customer later tries to book that same slot.
A hold is a persisted object with an expiry and explicit state. Its allocations count as occupied while it is live. When a new booking write acquires the branch lock, it expires overdue holds and deactivates their allocations before continuing.
A separate worker also performs cleanup. That worker helps housekeeping, but a delayed worker is not the only way an expired hold can be released. The write path handles overdue occupancy as part of the booking decision.
Confirmation checks the hold again. It also verifies that the relevant service version is still current and that the time remains eligible. If the reservation expired, the customer must choose a fresh time.
Evidence. Tests check both failed confirmation after expiry and release of conflicting expired allocations during a subsequent write. In the UI, the countdown communicates the current reservation window; the server remains the authority on expiry.
4. The booking succeeded, but the response disappeared
Scenario. The database commits a booking just before the customer’s connection drops. A browser retry should resolve the original intent without creating a second appointment.
Creation requests carry an idempotency key. The application stores that key within an operation scope alongside a representation of the request content. A repeated key with the same content returns the previously recorded result. Reusing the key for changed content produces a conflict.
This gives the client a way to reconcile an uncertain outcome. A timeout does not automatically mean the server failed to save. The interface therefore uses uncertainty and retry language for interrupted operations rather than announcing an unsupported success or encouraging a blind new booking.
Evidence. A PostgreSQL test repeats a committed request, verifies the same outcome and checks refusal when the content changes. This is a specific retry contract, not a claim that the application supports offline booking confirmation.
5. An attempted reschedule must not destroy the original booking
Scenario. A staff member moves a 14:00 appointment to 13:00, but another booking already occupies the destination. A duration extension can create the same problem at the end of an appointment.
The change operation checks the replacement interval, including its buffer and every required resource. It then replaces the active allocations and updates the appointment in a single transaction. A failed replacement rolls back, preserving the original state.
This is more dependable than cancelling the old booking first and hoping the new one succeeds. The merchant should never have to reconstruct a valid appointment because a proposed edit was rejected.
The detail sheet reports the conflict inline and tells the user that the original appointment is unchanged. The browser scenario also checks that a stale success toast does not obscure the later error.
A real conflict response from the local application. The original 14:00 reservation remains intact.
Evidence. Database tests verify failed moves, failed extensions and preservation of the original allocations. Browser verification exercises the rejected move and checks the visible original time afterward.
6. Two staff screens edit different versions of the same appointment
Scenario. One employee checks in a customer. Another employee still has the earlier appointment state open and tries to edit it.
Appointment changes carry the version the user last read. The server compares it with the current record. A successful change increments the version; an outdated request is refused and asks the user to refresh.
The current workspace refreshes every thirty seconds while visible, refreshes on window focus, and provides a manual refresh action. Its data-loading logic also avoids applying an older response after a newer request has completed.
That refresh model improves the working experience, while version checks handle the remaining gap between observation and action. WebSocket or server-sent live calendar updates are not implemented in this slice.
7. Local clock time is not a unique appointment identifier
Scenario. A timezone changes its daylight-saving offset. One local hour may disappear; another may occur twice. A customer may also view a branch’s schedule from a device set to a different timezone.
We store appointment instants in UTC and retain the branch’s IANA timezone for scheduling interpretation and display. Availability walks real UTC instants through the requested local day, then applies local business-hour rules.
That approach avoids manufacturing a nonexistent wall-clock time. When a local time occurs twice, the availability labels include its offset so the two instants can be distinguished.
Candidate starts use a fifteen-minute grid in the current implementation. Service duration, trailing buffer, opening hours and the booking horizon further restrict the valid results. The branch timezone, not the viewing device’s timezone, defines the business day.
Evidence. Tests exercise daylight-saving gaps and repeated instants. The case-study demo uses Africa/Cairo and EGP; those fixture choices do not establish an approved launch market or a real business address.
8. A waiting customer is not a confirmed time slot
Scenario. A customer arrives while the provider is busy. Staff need to record the arrival without promising a time that has not been acquired.
We represent the arrival as a queue ticket. It records the requested service and waiting customer but reserves no capacity. A staff member later selects an available time and promotes the ticket.
Promotion creates the appointment, allocates its resources and updates the ticket within the same transaction. The operation also checks the ticket’s current version and waiting status, so an already assigned ticket cannot quietly produce another reservation.
Evidence. The database suite checks that waiting tickets have no allocations and that promotion creates the timed booking atomically. The full browser journey adds a waiting customer and assigns a time through the UI.
// Explanatory model — not a production source extract.
async function reserveTime(command: BookingIntent) {
return database.transaction(async (tx) => {
await tx.branches.lock(command.branchId);
await tx.holds.releaseExpired(command.branchId);
const previous = await tx.operations.find(command.key);
if (previous) return replayMatchingIntent(previous, command);
await assertEligible(tx, command);
const hold = await tx.holds.create(command);
// Every required resource must fit, including its buffer.
await tx.allocations.reserveAll(hold);
await tx.operations.record(command.key, command, hold);
return hold;
});
}A simplified explanation, not a production code extract.
The booking is only the beginning.
EACH STATE MEANS SOMETHING
- 01Confirmed
- 02Checked in
- 03In service
- Completed
Service completed does not mean payment collected. Payment stays independent.
Designing the working day
Operational state remains explicit
The appointment model distinguishes confirmed, checked in, in progress, completed, cancelled and no-show states. The main service path is straightforward: confirm, check in, start, complete.
Cancellation releases the active resource allocation while retaining the appointment record. A no-show action has a grace-period check. Completion requires an explicit action and does not happen merely because the scheduled end time has passed.
Payment status remains a separate field. Completing a visit does not mark it paid. In the present customer journey, payment happens at the business. Merchant proof uploads, payment verification and a payment reporting module remain outside this delivered foundation.
This independence is useful even before payment features arrive. It prevents future reporting from assuming that service delivery and money received describe the same event.
A useful agenda without fabricated analytics
Today shows scheduled appointments, waiting tickets, in-service appointments and completed appointments. The numbers are derived from the current application data. The case-study screenshots use staged records, and their captions identify that fact.
We did not add revenue graphs, conversion percentages, review scores or growth claims to make the workspace look established. Those measures require defined business logic and real evidence.
On desktop, the interface uses a compact sidebar, a command bar and an agenda with supporting operational panels. On a phone, the navigation moves into a bottom bar and the appointment rows adapt to the narrower screen. Actions remain available through visible controls.
Mobile agenda with fictional appointments. The selected date is part of a staged demonstration.
A deliberate visual system
The application uses white and off-white surfaces, Ink text and restrained Coral emphasis. Inter supplies the primary typography. Hairline separators and compact spacing carry the structure. The implemented stylesheet removes the optional shadows from the initial token source.
Coral buttons use dark text. Status colors are paired with readable labels, so the user does not have to decode a color alone. Dialog and toast behavior comes from Radix primitives, styled through components owned by Bookalto.
The application currently has a light theme only. Changing the operating system’s color preference does not create a designed dark edition. Likewise, the Arabic editions of this case study are editorial deliverables; the application itself is still English. An RTL layout smoke check is narrower than a completed Arabic product localization.
What we customized
| Area | Softworks implementation | Foundation reused |
|---|---|---|
| Scheduling | Eligibility rules, availability generation, holds, resource allocation and appointment changes | PostgreSQL transactions and range constraints |
| Business setup | Saved drafts and atomic workspace creation | Next.js request handling and SQL persistence |
| Staff operations | Date-based agenda, queue promotion, attendance and conflict feedback | React and shared typed contracts |
| Guest booking | Service/time/review flow, hold countdown, confirmation and booking management | React, Luxon and reusable form components |
| Visual language | Ink/Coral tokens, no-shadow surfaces, responsive layout and operational labels | Tailwind CSS, Inter and Lucide |
| Interaction components | Bookalto wrappers, buttons, fields, sheets, errors and toast adapter | Radix interaction primitives |
| Verification | Product-specific contention, rollback, retry and browser scenarios | Vitest, Playwright and axe |
| Background processing | Internal event inbox and hold cleanup | PostgreSQL and a separate Node.js process |
The custom work is concentrated where the product needs specific behavior. We reuse established primitives for lower-level concerns and own the scheduling and operational decisions that make Bookalto distinct.
A shorter path to the front desk.
From a code, to “I’m here.”
A proposed bridge between an appointment pass and the front desk. QR or barcode lookup opens the right booking; staff review it and confirm arrival.
Reader integration is a future concept, outside the verified app. This example uses no camera or real booking data.
Consultation / 60 min
Illustrative, non-scannable symbolReady for the illustrative lookup.
Find
The code identifies an appointment; it is not an open access credential.
Review
Staff review the service, time and current status.
Confirm
Arrival remains an explicit action, independent of payment.
Built carefully. Moving forward.
Documented local checks. These are not adoption, revenue or production performance figures.
Verification and outcomes
What we observed locally
The latest local verification passed lint, strict type checking, fifteen PostgreSQL tests, the production build and the complete Edge browser scenario. The database tests create a separate temporary database and apply the application’s actual SQL migrations.
The browser journey covers merchant registration and setup, saved onboarding after reload, guest booking, remembered details, attendance, queue promotion, rejected appointment moves, service creation and publication controls. Automated accessibility checks reported no violations in the populated workspace and public booking states exercised by that scenario.
Responsive overflow checks cover 320, 360, 375, 390, 430, 768 and 1440 pixels. The browser suite also includes landscape and an RTL layout smoke check. These are valuable bounded checks. They do not replace a human screen-reader review, full translated content testing or real-device merchant acceptance.
What the results establish
| Observed result | Practical meaning | Limit of the evidence |
|---|---|---|
| One winner in each 100-request exclusive-slot test | Competing writes obey the tested occupancy rule | No production throughput or latency claim |
| Multi-resource failure rolls back | Partial reservations are not left behind in the tested transaction | Shared class seats are not implemented |
| Same-key creation replay resolves the existing result | A retry can recover an already committed intent | Not a general offline mode |
| Failed moves preserve original allocations | An unsuccessful edit does not discard a valid booking | No drag-and-drop calendar is delivered |
| Guest-to-staff browser journey passes | The core surfaces operate on persisted local data | Local delivery and fictional records only |
| Build and type checks pass | The reviewed application compiles and satisfies current static checks | Not deployment or pilot acceptance |
The outcome is a runnable, verified foundation connecting customer booking and daily staff operations. The project now has a coherent place to add the remaining product journeys without replacing its core scheduling model.
What we learned from the implementation
Correctness needs to be visible. A rejected edit is easier to trust when the interface states what remains unchanged. An uncertain request needs different language from a definite rejection. A queue ticket needs a different label from a confirmed appointment.
The data model benefits from equally precise distinctions. Holds differ from appointments; service time differs from occupied time; attendance differs from payment; internal event creation differs from message delivery. Preserving those distinctions reduces ambiguous behavior as the product grows.
The architecture also makes its tradeoffs visible. Coarse branch locking is understandable and testable, but future scale work should be informed by realistic workloads. The current availability calculation checks candidates directly; optimization should preserve the same authoritative write decisions.
The next product boundary
The broader Bookalto roadmap includes additional branch and team management, provider shifts and time off, richer calendar views, customer rescheduling UI, shared capacity, notifications, merchant payment workflows, feedback and Arabic localization.
Each extension needs its own acceptance evidence. A class booking needs final-seat contention tests. A new calendar interaction needs the same validated change transaction as the existing sheet. Outbound reminders need actual delivery and recovery checks. Arabic UI needs translated content and a complete RTL review.
The case study does not claim that these features are complete. It explains the working base from which they can be implemented.
Frequently asked questions
Is Bookalto a calendar widget or a booking platform?
The working foundation combines a public booking journey with merchant operations and persistent scheduling logic. Its staff view is currently an agenda. Advanced day/week/resource calendar views are future work.
Can online bookings and walk-ins compete for the same time?
Yes. Both paths use the same eligibility and resource allocation logic. The PostgreSQL tests exercise contention and verify that occupied exclusive resources cannot be allocated again.
Does the product support several required resources?
The scheduling model and tests support multiple required exclusive resources. A service requires all resources attached to it. A complete resource-management UI and alternative-provider selection are not yet delivered.
Are the screenshots from a working application?
Yes. They were captured from the local app using fictional records created through its normal flows and APIs. The records are demonstration data, not customer evidence.
Are payments processed inside Bookalto?
The present journey is pay-at-business. Appointment completion and payment status remain independent. The case study does not present a payment gateway or merchant proof-verification module as implemented.
Is there a dark or Arabic application edition?
The running app currently uses a light theme and English interface copy. This case study provides English, professional Egyptian Arabic and playful Egyptian Arabic reading editions. Product dark mode and full product localization remain future work.
Build around the difficult part of your product
A booking product becomes dependable when the edge cases receive the same attention as the main screen. Our work on Bookalto connects those decisions: the transaction that refuses an overlap, the interface that explains a conflict, and the test that proves the original appointment survives.
Working on a scheduling or service-operations product? Bring Softworks the workflow, its constraints and the moments where it breaks. We can help turn them into a focused implementation and a clear verification plan.
16 CAPTURES / THE WORKING PRODUCT
Little details. A whole working day.
From setting up a business to keeping an appointment. Actual screens with fictional records, ready to inspect at full resolution.
English, light-theme application. Images do not represent real customers.
16 screens
WebThe working day
Seven fictional appointments after guest confirmation; two queue tickets remain. Fictional local demo data.
PhoneChoose a service
Public service selection at 390px with illustrative EGP prices. Fictional local demo data.
PhoneFind a time
Available starts are returned by the server and displayed in Africa/Cairo. Fictional local demo data.
PhoneReview the booking
Review after local test verification; no code or credential is pictured. Fictional local demo data.
PhoneAppointment confirmed
Confirmation follows the real local database commit. Fictional local demo data.
WebA safe rejection
A real rejected move keeps Farah Demo's original appointment at 14:00. Fictional local demo data.






