Xây dựng website đặt lịch cao cấp với Claude Code
Xây dựng một website đặt lịch độc nhất cho salon tóc, tiệm cắt tóc nam hoặc studio massage, với lịch hẹn thật, email đặt lịch và bảng điều khiển cho chủ doanh nghiệp.
Lộ trình & tài nguyên
Yêu cầu
Claude Desktop
Claude Code xây dựng website của bạn trong ứng dụng desktop. Một lần build đầy đủ là một phiên dài, nên hãy dùng gói Claude trả phí còn dư dả hạn mức.
Tải Claude Desktop
https://claude.ai/download
Node.js
Tải bản LTS. Claude dùng nó để build và kiểm tra website của bạn.
Tải Node.js
https://nodejs.org/en/download
Tài khoản GitHub
Miễn phí. Mã nguồn website của bạn nằm ở đây, và Hostinger triển khai từ đây.
Tạo tài khoản GitHub
https://github.com/signup
Tài khoản Supabase
Gói Free là đủ. Gói này cho phép hai dự án đang hoạt động — nếu bạn đã có hai, hãy tạm dừng một dự án không dùng. Nơi đây lưu dịch vụ, giờ mở cửa, lịch đặt và thông tin đăng nhập bảng điều khiển của bạn.
Tạo tài khoản Supabase
https://supabase.link/r4gdwhi
Tài khoản Resend
Gói Free gửi 100 email mỗi ngày và 3.000 email mỗi tháng. Mỗi lượt đặt lịch gửi vài email: mã xác nhận, yêu cầu đã nhận, thông báo cho bạn và xác nhận lịch.
Tạo tài khoản Resend
https://resend.com/signup
Tên miền riêng của bạn
Website của bạn chạy trên tên miền này, và email đặt lịch được gửi từ nó. Một địa chỉ miễn phí như Gmail không gửi được các email này. Bạn mua tên miền kèm gói Hostinger? Hãy mua gói trước Bước 3.
Hosting Hostinger
Bạn cần hosting ở Bước 5, khi website của bạn lên mạng.
Chọn gói Hostinger
Dùng gói Business (Unlimited) hoặc gói Cloud — chỉ các gói này chạy được Node.js Web Apps: tối đa 5 trên Business, 10 trên Cloud.
Chọn mô hình kinh doanh đặt lịch
Chọn loại hình kinh doanh
Phần của bạn. Booking Engine giống nhau cho cả ba — thiết kế, câu chữ và dịch vụ mẫu được làm riêng cho loại bạn chọn.
Bạn kinh doanh loại hình nào?
Salon tóc
Cắt, nhuộm và tạo kiểu, với các dịch vụ salon có sẵn của engine.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a hair salon in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init --preset hair-salon`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with the engine's `hairSalonPreset` as `preview.preset`; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive hair salon, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Local clients, mostly 25–55, booking cuts, color and styling — most of them on a phone, often in a hurry. - **Tone:** Confident, warm and expert. Never clinical, never luxury cliché. - **Sections:** - Hero with the salon's personality and one clear “Book an appointment” action - Services and prices loaded from the engine (duration, price, “from” prices), grouped sensibly — with the owner's optional service photos (cut, colour, styling work) shown in the site's own style when a service has one, and a complete typeset menu without them - The team / stylists — placeholder bios and names the owner will edit, clearly marked - The salon space and approach (what makes this salon different) - Testimonials — clearly marked as placeholder examples, never presented as real reviews - Opening hours (from the engine) and location / contact (from the engine's business contact) - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Service first, then the date and time together, then details, then the email code, then confirmation - A real month calendar with month and year navigation that shows each day's status from the engine — open, only a few times left, fully booked, closed or past — loaded with one getAvailability call per visible month (INTEGRATION.md §8 and the calendar helpers in §9); on phones a week strip with the same statuses and a “Month” view - Times grouped as Morning / Afternoon / Evening (a segmented switch with counts when there are many); the chosen time continues inline (“1:30 PM — Continue”), and a “soonest time” shortcut comes from data already loaded - Keep the chosen service, duration, price, date and time visible at every step — a summary beside the calendar on wide screens, a sticky bar on phones - The code step feels like part of the salon's flow: the masked address the code went to, one large code field, “Send a new code”, “Choose another time”, and a reminder to check spam - Designed states, not error dumps: times loading, times that could not load (Try again), a time just taken by someone else (fresh times), a fully booked day (the next available day), a closed day, and online booking switched off (contact details) - Pending wording: “Request received — we'll confirm your appointment shortly.” Confirmed wording: “You're booked.” - **Imagery:** Real, coherent photography is part of the design, not an extra: the hero, the salon space, details of the work (hands, tools, colour, texture) and the storefront, all in one photographic direction. Show hair and craft, never posed stock smiles. Never present a photographed person as one of the salon's stylists: sample team cards show work, not faces, and stay labelled as samples until the owner adds real portraits. Motifs (scissor, comb, hair-texture curves) frame the photos; they do not replace them. Service photos are the owner's (added per service in the dashboard): present them as part of the menu's design, never as stock fillers. - **Avoid:** - A generic SaaS landing page look, purple gradients, glassmorphism everywhere - Invented awards, certifications, review counts or ratings - Medical or clinical styling - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “clients” and bookings “appointments”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Search terms that work: “hair salon interior”, “hairdresser cutting hair close up”, “hair colour bowl brush”, “salon chair mirror”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Tiệm cắt tóc nam
Cắt tóc, fade, tỉa râu và cạo râu, đặt lịch nhanh từ điện thoại.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a barbershop in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with this site's own preset as `preview.preset` — write it in `src/lib/preset.ts` and export it as `sitePreset` (BUILD-A-SITE §1) with the words “clients” and “appointments”; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. - Write `sitePreset` from this sample data (prices are US dollars: use the local currency at a plausible local price level; `id` "barbershop", `label` "Barbershop"): - Classic Cut · 30 min (+10 min buffer) · 40 · “Scissor or clipper cut, styled and finished with a neck shave.” - Skin Fade · 45 min (+10 min buffer) · 45 · “Tight fade blended to the skin, shaped on top, sharp line-up.” - Beard Trim & Line-up · 20 min (+5 min buffer) · 25 · “Shape, trim and a hot-towel line-up for the beard.” - Cut & Beard · 60 min (+10 min buffer) · 60 · “A full haircut and beard shape in one sitting.” - Hot Towel Shave · 40 min (+10 min buffer) · 45 · “Straight-razor shave with hot towels and a cool finish.” - Buzz Cut · 20 min (+5 min buffer) · 25 · “One length all over, cleaned up at the neck and ears.” - Kids Cut (under 12) · 30 min (+10 min buffer) · 28 · “A patient, quick cut for younger clients.” - Opening hours: Tuesday to Friday 10:00–19:00, Saturday 09:00–17:00; closed Sunday and Monday - Policies: 15-minute slot interval, at least 1 hour's notice, up to 30 days ahead, the owner confirms each request, a phone number is required; every other policy as in BUILD-A-SITE §1 The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} presets={[sitePreset]} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive barbershop, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Neighbourhood regulars and newcomers, mostly 18–50, who book a cut every three to five weeks — almost always on a phone, often the same day. - **Tone:** Easygoing, sharp and confident; plain-spoken and welcoming to everyone. No macho bravado, no “gentlemen's club” clichés. - **Sections:** - Hero with the shop's character and one clear “Book a chair” action, plus the soonest open time for the first service on the menu (named) when it is known - The menu: services, durations and prices loaded from the engine, readable at a glance like a board on the wall — with the owner's optional service photos (cuts, fades, beard work) shown in the site's own style when a service has one, and a complete menu without them - The barbers — placeholder names and short bios the owner will edit, clearly marked as samples (portraits come later from the owner) - The shop: the chairs, the music, the routine of a visit — what a first-timer can expect - Kind words — clearly marked as placeholder examples, never presented as real reviews - Opening hours (from the engine) and location / contact (from the engine's business contact), with directions - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Service first, then the date and time together, then details, then the email code, then confirmation — fast enough to finish standing in line for coffee - Make the soonest time obvious: a “Next open: today 4:15 PM” shortcut from data already loaded, and a week view that opens on today — on a closed day, say so and offer the next open time - Short services mean many times: group them Morning / Afternoon / Evening with counts, and keep the list scannable on a phone - There is one shared calendar: never offer “choose your barber” or show staff availability — the engine does not book per barber - Keep the chosen service, duration, price, date and time visible at every step — a summary beside the times on wide screens, a sticky bar on phones - The code step stays in the shop's voice: where the code went (masked), one large code field that accepts a pasted code, “Send a new code”, “Choose another time”, and a reminder to check spam - Designed states: times loading, times that could not load (Try again), a time just taken (fresh times), a fully booked day (the next open day), a closed day, and online booking switched off (call or message the shop) - Pending wording: “Request received — we'll confirm your chair shortly.” Confirmed wording: “You're booked. See you in the chair.” - **Imagery:** Real photography of the craft is part of the design: clippers and straight razors, the chair, the lather, the line-up, a door, window or street at dusk (no other shop's name, no barber pole, no identifiable people; if no such licensed photo exists, a designed frame such as a painted window sign) — one coherent photographic direction. Never present a photographed person as one of the shop's barbers: sample barber cards show tools or work, not faces, and stay labelled as samples until the owner adds real portraits. Graphic motifs (rules, numerals, signage lettering) frame the photos; they do not replace them. Service photos are the owner's (added per service in the dashboard): present them as part of the menu's design, never as stock fillers. - **Avoid:** - Barber-pole stripes, skulls, mustache icons and other stock barbershop clichés - A generic SaaS landing page look, purple gradients, glassmorphism - Invented awards, review counts, ratings or “best in town” claims - A “choose your barber” step or per-barber availability (not supported by the engine) - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “clients” and bookings “appointments”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Free photos already checked for this niche, to start from (confirm the licence on each page; replace any): https://www.pexels.com/photo/barber-perfecting-a-fade-haircut-close-up-39559270/ (a fade being cut, close up (hero)); https://www.pexels.com/photo/vintage-barber-shop-interior-with-classic-chairs-37764947/ (a row of classic barber chairs); https://www.pexels.com/photo/customer-at-barber-16553361/ (a lathered shave in progress); https://www.pexels.com/photo/tools-at-barbershop-7697712/ (clippers and tools on a counter); https://www.pexels.com/photo/hairdresser-scissors-and-tools-10317439/ (scissors and combs); https://www.pexels.com/photo/gray-hand-tool-1319459/ (a brass shaving bowl and razors). Search terms that work: “barber fade close up”, “barbershop interior chairs”, “straight razor shave”, “barber tools clippers”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Studio massage & chăm sóc sức khỏe
Massage và liệu trình chăm sóc, với trải nghiệm đặt lịch nhẹ nhàng.
Build a complete, premium booking website for **{{BUSINESS_NAME}}**, a massage & wellness studio in {{CITY}}, in this empty folder. The booking system is an installed package — the **Profit Studio Booking Engine**. It owns availability, bookings, booking emails and the owner's dashboard. You design and build the public website around it, and nothing the engine provides. ## 1. Set up 1. Scaffold a Vite + React + TypeScript app **in the current folder**: `npm create vite@latest . -- --template react-ts --no-interactive`, then `npm install`. create-vite stops without an error when the folder is not empty — a `.DS_Store` or a `.claude/` folder is enough — so check that `package.json` now exists. If it does not, scaffold into a new subfolder the same way, move all of its files (dotfiles included) up into this folder without overwriting or deleting anything already here, and remove the empty subfolder. Remove the template's demo content. 2. `npm install --save-exact @profitstudio/booking-engine@2.0.0-beta.1`, then `npx booking-engine init`. 3. Read `node_modules/@profitstudio/booking-engine/docs/BUILD-A-SITE.md` once: it has every file to wire, the booking API and the error table. Open `docs/INTEGRATION.md` only at a section it names, and nothing else in the package. 4. Wire the engine as BUILD-A-SITE §1 shows, with this site's own preset as `preview.preset` — write it in `src/lib/preset.ts` and export it as `sitePreset` (BUILD-A-SITE §1) with the words “guests” and “sessions”; in `preview.business` use `{{BUSINESS_NAME}}`, the time zone `{{TIMEZONE}}` and a plausible placeholder `contact` in {{CITY}}. - Write `sitePreset` from this sample data (prices are US dollars: use the local currency at a plausible local price level; `id` "massage-studio", `label` "Massage & Wellness Studio"): - Swedish Massage — 60 min · 60 min (+15 min buffer) · 95 · “Long, flowing strokes to ease tension and settle the mind.” - Swedish Massage — 90 min · 90 min (+15 min buffer) · 135 · “The full-body Swedish session with time for slower, deeper work.” - Deep Tissue — 60 min · 60 min (+15 min buffer) · 105 · “Firm, focused pressure for tight shoulders, back and legs.” - Deep Tissue — 90 min · 90 min (+15 min buffer) · 150 · “Unhurried deep work on the areas you choose.” - Hot Stone — 75 min · 75 min (+20 min buffer) · 125 · “Warm basalt stones and long strokes to release and soften.” - Pregnancy Massage — 60 min · 60 min (+15 min buffer) · 100 · “Side-lying comfort and gentle pressure, from the second trimester.” - Reflexology — 45 min · 45 min (+15 min buffer) · 70 · “Pressure-point work on the feet in a reclined chair.” - Opening hours: Monday to Friday 10:00–20:00, Saturday 09:00–17:00, Sunday 10:00–16:00 - Policies: 15-minute slot interval, at least 4 hours' notice, up to 60 days ahead, the owner confirms each request, a phone number is required; every other policy as in BUILD-A-SITE §1 The dashboard on `/admin/` mounts `<BookingAdmin engine={engine} siteUrl="/" content={siteContent} presets={[sitePreset]} />` and gets only your brand accent (light and dark) in `src/admin-theme.css`. One public page (sections and an in-page booking flow), no client-side routes. No backend yet: no Supabase project, no `supabase` commands, no `.env.local` — the site runs in preview mode with a visible “Preview — sample data, bookings are not saved” banner. ## 2. Design — the creative part Make it feel like a real, distinctive massage & wellness studio, not a template: - **Style direction:** {{STYLE_DIRECTION}} - **Audience:** Adults 25–65 looking for relief from everyday tension or simply an hour of quiet — often booking in the evening on a phone, sometimes for the first time. - **Tone:** Calm, warm and reassuring; clear and practical. Never clinical, never mystical, and never promise health outcomes. - **Sections:** - Hero that slows the visitor down, with one clear “Book a session” action - Treatments loaded from the engine — each length is its own service (e.g. 60 and 90 minutes), shown as a calm, readable list with durations and prices — with the owner's optional service photos in the site's own style when a service has one, and complete without them - What to expect: arriving, the room, comfort and pressure, privacy — written for a first-timer - The therapists — placeholder names and short bios the owner will edit, clearly marked as samples - Questions (what to wear, arriving early, cancelling) as plain text the owner can edit - Opening hours (from the engine) and location / contact (from the engine's business contact) - The booking flow - Footer with a discreet “Staff login” link to /admin/ - **Booking experience:** - Treatment and length first, then the date and time together, then details, then the email code, then confirmation - A calm calendar: the next open days first, times grouped Morning / Afternoon / Evening; never a countdown or scarcity pressure - There is one shared calendar: never offer “choose your therapist” or show per-therapist availability - The note field is optional and invites preferences (pressure, areas to avoid) — never ask for medical history or health details in the form - Keep the chosen treatment, length, price, date and time visible at every step - The code step is gentle and clear: where the code went (masked), one large code field that accepts a pasted code, “Send a new code”, “Choose another time”, a reminder to check spam - Designed states: loading, could not load (Try again), a time just taken, a fully booked day (the next open day), a closed day, online booking switched off (call or email the studio) - Pending wording: “Request received — we'll confirm your session shortly.” Confirmed wording: “Your session is booked.” - **Imagery:** Quiet, tactile photography: hands at work, linen, warm stones, oil, soft light across a treatment room — one coherent direction, little colour, lots of air. Never present a photographed person as one of the studio's therapists: sample therapist cards show the room or hands, not faces, and stay labelled as samples. No before/after imagery. Service photos are the owner's (added per service in the dashboard): present them as part of the design, never as stock fillers. - **Avoid:** - Spa clichés: orchids, Buddha statues, stacked stones, lotus icons - Medical claims, diagnoses, “cures” or before/after promises - Countdown timers, scarcity pressure or pushy upsells - A “choose your therapist” step or per-therapist availability (not supported by the engine) - Hard-coded services, prices or opening hours (they come from the engine) - **Words:** call customers “guests” and bookings “sessions”. How: - **Identity first.** A short `DESIGN.md`: the concept in one sentence, a display face with character and a readable text face (both self-hosted with `@fontsource`), colour tokens with every text/background pair at 4.5:1 or better, a type and spacing scale, how motion behaves. Every value lives in one CSS file of custom properties. Two businesses in this niche with different style directions must not look like the same layout recoloured. Plain CSS, no UI kit. - **Composition.** Varied rhythm — full-bleed moments, asymmetric grids, one focal point per screen; designed for phones first, from 320 px to wide screens; keyboard-accessible with visible focus; motion of 150–600 ms with a purpose, off under `prefers-reduced-motion`. - **Engine data, never hard-coded:** services, prices, opening hours, contact details and availability (BUILD-A-SITE §3). “Open now” lines come from `getOpeningTimes`. Show each service's optional `image` in your own style when it has one, and make the services section complete with none. - **Owner-editable copy.** Declare the texts, photos, logo, brand colours and heading style the owner may change in `src/content/site.ts` (BUILD-A-SITE §2), with your copy and photos as defaults, render the page from it as text, and bake it in with the Vite plugin. - **Photos.** Free, properly licensed photographs (Unsplash or Pexels licence), at most six, downloaded into `photos/source/` and recorded in `PHOTOS.md` — never hot-linked, never a photographed person presented as staff (sample team cards show the work and say “Sample photo”; content-list images cannot have defaults, so sample cards may be text-only or share one site image). Free photos already checked for this niche, to start from (confirm the licence on each page; replace any): https://unsplash.com/photos/massage-on-leg-with-white-towel-yBLLzNcA6GU (hands at work over a white towel (hero)); https://www.pexels.com/photo/settee-by-the-window-6628520/ (a treatment table by a tall window); https://unsplash.com/photos/hands-massaging-a-persons-forearm-with-gentle-pressure-YBfzGCJjLuI (hands working on a forearm). Search terms that work: “massage hands towel”, “treatment room window”, “spa linen stones”. Prepare them with `npx booking-engine photos` (BUILD-A-SITE §5): AVIF + WebP at two or three widths, `width`/`height` on every `<img>`, fixed `aspect-ratio` frames with `object-fit: cover`, the hero eager with `fetchpriority="high"`, everything else lazy. When no fitting photo exists, design the frame without one. - **The booking flow** follows BUILD-A-SITE §4 with the calendar helpers: server slots only, the business time zone, the two steps (`requestBooking`, then the code and `verifyBooking`), every error in the table, contact details when online booking is off, success only after `verifyBooking` returned `ok: true`. On a phone: the step's name always visible, the choice and price in view, 44 px targets, the right input types, inline validation, a loading state on the control that waits, a code field that accepts a pasted code, focus on each new step's heading. - **Every “Book” button always answers.** Whatever the booking section is showing — loading, the flow, online booking off, no services, a calendar that failed to load — each Book/CTA button on the page scrolls to the booking section and moves focus to its heading, so the visitor sees that state's message and the shop's contact details. Never route a CTA only through the flow component (it is not on the page in those states), and never leave a click that does nothing. Check it with online booking off as well as on. ## 3. Work efficiently The viewer's Claude plan pays for every step, so keep the session lean: - Read BUILD-A-SITE.md once; don't re-read files you wrote or explore the package. - Photos: start from the leads above or spend at most about ten minutes searching; judge candidates on one contact sheet of small thumbnails, not one by one at full size. - Screenshots: about eight in all — desktop and a 390 px phone (device emulation), downscaled to at most 1280 px wide. Don't capture very long pages at full resolution. - Don't simulate the engine's error answers to see every state: build each state from the error table and check it in the code. - `npm run build` and `npx booking-engine doctor --dist dist` once midway and once at the end; afterwards fix and rerun only what failed. ## 4. Finish - `CLAUDE.md` in the project root: the engine's boundaries (from `BOOKING_ENGINE.md`), where the design lives, and that new booking features need a new engine version, never custom database code. - Done when: `npm run build` and `npx booking-engine doctor --dist dist` pass; every field in `src/content/site.ts` is used and the page looks as designed with the defaults; the key sections show real photos (`PHOTOS.md`, AVIF/WebP with dimensions; `DESIGN.md` matches the tokens); `npm run dev` serves `/` with the preview banner and a booking flow that reaches the code step and the honest preview message, and `/admin/` shows “Connect your backend”. Stop the dev server. - End with a short summary: what you built, the design choices, and anything in the engine's documentation that was unclear or wrong.
Tùy chỉnh và sao chép prompt
Phần của bạn.
Mở Customize Prompt trên prompt ở trên Nhập tên doanh nghiệp và thành phố — múi giờ ban đầu là múi giờ của bạn, nên chỉ đổi nếu doanh nghiệp ở nơi khác Chọn một định hướng phong cách — mỗi lựa chọn cho website của bạn một diện mạo khác Sao chép prompt — bạn sẽ gửi nó cho Claude ở Bước 2
Warm editorial — màu espresso, đồng và kem, font serif thanh lịch và bố cục kiểu tạp chí
Modern minimal — gần trắng và xám than với một màu nhấn tự tin và lưới chặt chẽ
Bold urban — đen sâu, xám bê tông và một màu nhấn rực rỡ, với độ tương phản mạnh
Soft natural — xanh xô thơm, màu cát và hồng phấn, chữ bo tròn, êm dịu và thoáng đãng
Heritage modern — xanh chai, đỏ sẫm và đồng thau trên nền giấy ấm, cổ điển nhưng không phô trương
Industrial clean — đen, thép và xám bê tông với một màu tín hiệu và chữ số lớn
Neighbourhood warm — kem, đất nung và màu mực, kiểu chữ thân thiện và khoảng cách thoải mái
Quiet Nordic — màu yến mạch, đá và xanh xô thơm nhạt trên nền trắng ấm, lề rộng và chuyển động nhẹ nhàng
Warm earth — màu đất sét, đất nung và ô liu đậm, tiêu đề bo tròn và hình khối tự nhiên
Night calm — xanh mực đậm và tông nến ấm, mờ tối và gần gũi
Prompt cài Booking Engine đúng phiên bản 2.0.0-beta.1, phiên bản đã dùng để kiểm thử hướng dẫn này. Đừng đổi thành latest.
Tạo website với Claude Code
Mở một thư mục trống
Phần của bạn.
Claude Desktop Code Local
Tạo một thư mục mới, trống cho website của bạn, ví dụ trong Documents Bắt đầu một phiên Local và chọn thư mục đó Nếu Claude Desktop hỏi, hãy tin cậy thư mục này
Gửi prompt
Phần của Claude Code — bạn chấp thuận các thao tác và chờ.
Dán prompt bạn đã sao chép ở Bước 1 và gửi Nhấn Allow khi Claude xin chạy một lệnh Để Claude làm việc — một lần build đầy đủ là một phiên dài, nên hãy giữ phiên mở
Tạo dự án website và cài Booking Engine
Thiết kế website: phông chữ, màu sắc, bố cục và câu chữ cho doanh nghiệp của bạn
Tìm ảnh miễn phí, có giấy phép và chuẩn bị chúng cho web
Xây dựng luồng đặt lịch và trang bảng điều khiển của chủ doanh nghiệp
Kiểm tra mọi thứ bằng npm run build và npx booking-engine doctor
Nếu Claude dừng vì bạn đã chạm giới hạn sử dụng, hãy chờ đến khi giới hạn được đặt lại, rồi gõ continue trong cùng phiên.
Xem website của bạn
Phần của Claude Code, rồi đến phần của bạn — Claude khởi chạy bản xem trước, bạn xem.
Cho tôi xem website
Chạy website trên máy tính của bạn và cho bạn địa chỉ của nó.
Start the website's dev server with npm run dev and open it in the Browser pane. Then give me its local address. Don't change any files.
Xem bản xem trước trong Browser pane của Claude Desktop, hoặc mở địa chỉ Claude đưa cho bạn trong trình duyệt Kiểm tra thiết kế trên cửa sổ rộng và cửa sổ hẹp Nhấn một nút Book: luồng đặt lịch mở ra và đi đến bước yêu cầu mã email Mở /admin/ — thêm vào cuối địa chỉ, hoặc nhờ Claude mở: trang hiển thị Connect your backend
Banner ở trên cùng ghi Preview: đây là dữ liệu mẫu và chưa có lịch đặt nào được lưu. Muốn thay đổi? Hãy nhờ Claude trong cùng phiên trước khi đi tiếp.
Kết nối Supabase và Resend
Tạo dự án Supabase của bạn
Phần của bạn — trong tài khoản Supabase của bạn.
Bước này có ba phần: Supabase, rồi email, rồi một prompt kết nối mọi thứ. Các giá trị bạn nhập được lưu trong hướng dẫn này và dùng lại ở Bước 5, nên bạn chỉ gõ mỗi giá trị một lần.
Mở Supabase Dashboard
https://supabase.com/dashboard
Nhấn New project, chọn khu vực gần doanh nghiệp của bạn và gói Free Lưu mật khẩu cơ sở dữ liệu vào trình quản lý mật khẩu — không bao giờ gửi nó cho Claude Khi dự án sẵn sàng, nhấn Connect và dán Project URL cùng Publishable key vào bên dưới
Tạo thông tin đăng nhập bảng điều khiển
Phần của bạn — trong tài khoản Supabase của bạn.
Supabase Authentication Users
Nhấn Add user, rồi Create new user Nhập email và một mật khẩu mạnh Để nguyên Auto Confirm User được đánh dấu và tạo người dùng
Supabase Authentication Sign In / Providers
Tắt Allow new users to sign up và lưu — chỉ bạn đăng nhập vào bảng điều khiển
Đăng nhập Supabase CLI
Phần của bạn — một lần, trước khi kết nối.
Nhấn Terminal trên thanh tiêu đề của phiên Chạy lệnh bên dưới Chấp thuận đăng nhập trong cửa sổ trình duyệt vừa mở
npx supabase@2 login
Nếu Claude nhờ bạn tự liên kết dự án, hãy chạy lệnh nó đưa ở đây và gõ mật khẩu cơ sở dữ liệu vào Terminal — không bao giờ gõ vào khung chat.
Xác minh tên miền gửi email
Phần của bạn — trong Resend và DNS của tên miền.
Email đặt lịch được gửi từ tên miền của chính bạn — chính tên miền mà website của bạn sẽ dùng ở Bước 5. Chưa có tên miền? Hãy mua một tên miền trên Hostinger, rồi quay lại:
Hostinger Domains
Mở Resend Domains
https://resend.com/domains
Nhấn Add domain và nhập tên miền gửi email hiển thị ở trên Chọn khu vực gần khách hàng của bạn nhất Để mở trang có các bản ghi DNS
Hostinger Domains DNS / Nameservers
Chọn tên miền của bạn và thêm từng bản ghi Resend hiển thị, với cùng loại và giá trị Quay lại Resend, nhấn Verify DNS records
Việc xác minh thường mất dưới 15 phút và có thể kéo dài đến 72 giờ. Trong lúc chờ, hãy tiếp tục các bước tiếp theo. Tên miền được quản lý ở nơi khác? Hãy thêm các bản ghi tương tự ở đó và giữ nguyên các bản ghi email hiện có.
Thêm secret email vào Supabase
Phần của bạn — key chỉ được đưa vào Supabase, không bao giờ vào khung chat.
Trong Resend, mở API Keys và nhấn Create API key Chọn Sending access và tên miền đã xác minh, rồi sao chép key — Resend chỉ hiển thị nó một lần Trong Supabase, mở Edge Functions, rồi Secrets, và thêm hai mục này
RESEND_API_KEY
Giữ bí mật key Resend
Chỉ dán nó vào secret Edge Function của Supabase. Không bao giờ gửi nó cho Claude, không bao giờ đưa vào file website hay prompt, và không bao giờ thêm vào Hostinger.
Nhờ Claude kết nối mọi thứ
Phần của Claude Code — nó dừng lại mỗi khi có việc bạn cần tự làm.
Trong cùng phiên Claude, sao chép prompt này và gửi đi:
Claude hỏi trước mỗi hành động — hãy nhấn Allow. Claude dừng lại khi đến lượt bạn.
Bạn đã xong khi Claude báo Connected. Email đặt lịch bắt đầu được gửi khi Resend hiển thị tên miền của bạn là Verified — email thử ở Bước 4 sẽ xác nhận điều đó.
Thiết lập bảng điều khiển và thử đặt lịch
Mở bảng điều khiển
Phần của Claude Code, rồi đến phần của bạn — Claude khởi chạy website, bạn đăng nhập.
Chạy website đã kết nối của tôi
Chạy website của bạn, giờ đã kết nối với Supabase, và cho bạn địa chỉ của website và bảng điều khiển.
Stop any dev server that is still running, so the new .env.local is read. Then start the website with npm run dev, open /admin/ in the Browser pane, and give me two addresses: the website and its /admin/ dashboard. Don't change any files.
Mở bảng điều khiển và đăng nhập bằng thông tin đăng nhập bạn đã tạo ở Bước 3. Website của bạn vẫn chạy trên máy tính của bạn, nhưng mọi thứ ở đây đều đã là thật: bảng điều khiển, lịch đặt và email dùng dự án Supabase của bạn, và website thật của bạn ở Bước 5 cũng dùng cùng dữ liệu đó.
Hoàn tất thiết lập
Phần của bạn — trong bảng điều khiển.
Trước tiên, chọn nơi nhận thông báo yêu cầu mới
Không có địa chỉ nhận thông báo, sẽ không ai được báo về yêu cầu mới, và không thể bật đặt lịch trực tuyến.
Làm theo Finish setting up ở đầu bảng điều khiển — mục này hướng dẫn bạn qua thông tin doanh nghiệp, dịch vụ, giờ mở cửa và email Trong Email & online booking, đặt Send new-request alerts to là hộp thư của chính bạn, rồi nhấn Send test email và kiểm tra xem email đã đến chưa
Trước khi bật đặt lịch, hãy nhấn từng nút Book trên website: nút phải chuyển đến phần đặt lịch và hiển thị thông tin liên hệ của bạn. Nút không làm gì là lỗi — hãy nhờ Claude sửa.
Bật đặt lịch trực tuyến
Đặt thử một lịch hẹn
Phần của bạn — với vai trò khách hàng, rồi với vai trò chủ doanh nghiệp.
Trên website của bạn, đặt một dịch vụ bằng địa chỉ email của chính bạn. Sau đó đánh dấu từng mục khi bạn thấy nó xảy ra:
Mã 6 chữ số đã đến, và website báo rằng yêu cầu của bạn đã được nhận
Thông báo yêu cầu mới đã đến địa chỉ nhận thông báo của bạn
Bạn đã nhấn Confirm request trong bảng điều khiển, và email xác nhận đã đến
Khung giờ đó không còn được mở để đặt trên website của bạn
Không nhận được email? Hãy kiểm tra thư rác trước, rồi mở Email & online booking trong bảng điều khiển: nó hiển thị trạng thái từng email và những gì cần sửa.
Thay đổi câu chữ và ảnh trên website
Phần của bạn — trong bảng điều khiển, không cần code.
Mở Website trong bảng điều khiển Đổi tiêu đề chính hoặc ảnh chính Nhấn Preview để kiểm tra, rồi nhấn Publish Tải lại website — thay đổi đã xuất hiện
Claude đã thiết kế website; bảng điều khiển chỉnh sửa câu chữ, ảnh và màu thương hiệu mà Claude cho phép chỉnh sửa. Muốn có một phần mới hoặc bố cục khác, hãy nhờ Claude.
Triển khai lên Hostinger
Tạo một repository GitHub riêng tư
Phần của bạn — trong tài khoản GitHub của bạn.
Website của bạn đã chạy trên máy tính. Giờ là lúc đưa nó lên mạng: GitHub lưu giữ mã nguồn, còn Hostinger build và phục vụ website — với cùng dữ liệu Supabase bạn vừa kiểm thử.
Tạo repository GitHub
https://github.com/new
Đặt tên theo doanh nghiệp của bạn và chọn Private Để tắt README, .gitignore và license, để repository trống Nhấn Create repository và sao chép địa chỉ của nó
Đẩy website lên GitHub
Phần của Claude Code — bạn chấp thuận lần push.
Đẩy website của tôi lên GitHub
Kiểm tra để chắc chắn không có secret nào bị kèm theo, commit website của bạn và push lên repository mới.
Put this website on GitHub at {{REPOSITORY_URL}}, on the main branch. Stop the dev server first. Before committing, check that .gitignore keeps node_modules, dist, .env, .env.local and supabase/.temp/ out of Git, and run npx booking-engine doctor. Show me the list of files you will commit and make sure no secret, key or password is in any of them. Then initialise Git if needed, commit everything with a clear message and push it to main. Never push to any other repository. If GitHub needs me to sign in, tell me exactly what to do, and never ask me to paste a password or token into the chat. When it's done, tell me the commit you pushed.
Địa chỉ kho của bạn
https://github.com/your-name/your-booking-website
Tạo Web App của bạn
Phần của bạn — trong Hostinger. Chưa có hosting? Hãy mua gói ở phần Yêu cầu.
Hostinger Websites Create site Web App
Nếu Hostinger cho chọn tên miền, hãy chọn tên miền của bạn — nếu không, ứng dụng của bạn sẽ bắt đầu trên một tên miền tạm thời Chọn Import Git Repository Cấp quyền cho GitHub và chọn repository Claude vừa push
Cấu hình và triển khai
Phần của bạn — trong Hostinger.
Giữ nguyên các thiết lập Vite mà Hostinger phát hiện Chọn Node.js 22 trở lên Đảm bảo lệnh build là npm run build và thư mục đầu ra là dist
Trước khi triển khai, tìm Environment variables trong Review build settings, nhấn Edit và thêm hai biến này — là các giá trị của bạn từ Bước 3, sẵn sàng để sao chép. Chúng được tích hợp vào website, nên nếu đổi một giá trị sau này thì cần triển khai lại.
Chỉ giá trị công khai
Không bao giờ thêm Secret key của Supabase hay key Resend vào đây. Mọi thứ bắt đầu bằng VITE_ đều hiển thị trong trình duyệt, còn secret email của bạn thì ở lại trong Supabase.
Nhấn Finish, rồi Deploy, và chờ build hoàn tất
Nếu quá trình build dừng lại và nêu tên một biến, hãy sửa giá trị của biến đó trong Hostinger và triển khai lại.
Thêm địa chỉ website thật vào thông báo cho chủ doanh nghiệp
Phần của Claude Code.
Kiểm tra website thật của bạn
Phần của bạn.
Mở tên miền của bạn — biểu tượng ổ khóa hiện ra và không còn banner Preview Kiểm tra dịch vụ, giá và giờ mở cửa Trên điện thoại, đặt thử một lịch hẹn và nhập mã Mở /admin/ trên website thật, đăng nhập và xác nhận yêu cầu Kiểm tra xem cả thông báo và email xác nhận đều đã đến
Từ giờ, hãy thay đổi dịch vụ, giá, giờ mở cửa cùng câu chữ và ảnh trên website trong bảng điều khiển — thay đổi có hiệu lực ngay. Với thay đổi về thiết kế hoặc code, hãy nhờ Claude và chấp thuận lần push: Hostinger build lại website mỗi khi có push lên main. Công cụ tìm kiếm sẽ thấy tiêu đề hoặc mô tả trang mới sau lần build tiếp theo.