[CODE]

第三種狀態,業主設定起來零成本

民宿官網的空房表原本只有兩種狀態:可訂、已訂。業主需要第三種——連假鎖起來、引導客人打電話的日期。難的不是狀態本身,是怎麼讓忙碌的業主不可能設錯。

1 min read AI 生成
astro typescript bnb ux system-design

這家民宿官網的空房表原本只有兩種狀態:可訂,跟已訂。可訂是灰綠色,已訂是木紋斜紋。它讀業主的 Google 日曆(透過 ICS),只留下每個事件的起訖日期,其他全部丟掉——不讀標題、不讀與會者、不讀說明。這種刻意的「看不見」就是隱私保證:來源日曆裡有真實房客的姓名,因為業主本來就拿它記真實訂房,所以這個模組打從一開始就設計成那些資料絕對不會流到公開頁面上。

後來需求變了。長假的週末,業主不想拆賣單晚——這是整棟包棟的民宿,夾在兩筆訂單中間、孤零零的一個週六就是死掉的庫存。他要那些日期被「鎖起來」,但不要顯示成「已訂」。他要它顯示成「洽詢」——一個叫客人拿起電話或 LINE 的訊號,讓他親自篩選、看能不能把行程接成比較長的一段。

這就是第三種狀態。有意思的不是狀態本身,是它從哪裡來。

為什麼不開第二本日曆

最直覺的做法是每館再開一本 ICS:一本「封鎖日期」日曆疊在訂房日曆上面。程式碼很乾淨,操作上卻是災難。

這個品牌有兩館,業主本來就在整批物件的訂房日曆之間切來切去。每館多一本日曆,就是多四個要接的 feed、多四個環境變數,而且真正的成本是——業主每次想鎖一個日期,都得在手機裡切換日曆。整套把官網跟真實訂房接起來的系統,只有在業主天天用它的前提下才成立。每多一本他得記得打開的日曆,就多一個不用它的理由。

第二個被否掉的選項比較細膩:Google 日曆每個事件都有「顯示為有空/忙碌」的旗標(ICS 裡叫 TRANSP)。我們可以讀它,把「有空」的事件當成鎖定狀態。但那個旗標藏在事件編輯畫面點三層才找得到。業主在兩通客人電話之間封鎖一個連假週末,不可能每次都穩定找到它,忘了設的時候,也不會有任何提示。

整個機制就是標題裡的一個詞

最後上線的設計:同一本日曆、同一套日常流程。要鎖一個日期,業主建一個整天事件,標題去掉前後空白後,剛好是一個詞——就是官網對那個狀態顯示的那個詞。日曆上其他任何東西都算真實訂房。

最後這句話就是整個安全論證。關鍵字比對是一個布林判斷——標題等於關鍵字,或不等於——結果一算完立刻丟掉。我們現在讀 SUMMARY 欄位,這是模組以前拒絕碰的。這是隱私規則的刻意放寬,也照這樣記錄下來:讀標題、跑一次精確比對、留下布林值、丟掉字串。說明、地點、與會者一律不讀。隱私保證只縮小了一個詞的表面積,再多一寸都沒有。

失敗的方向才是重點。如果業主把關鍵字打錯,事件比對不到——就會掉回預設值,顯示成已訂。打錯字是往少賣的方向倒:一個本該寫「洽詢」的日期,變成寫「客滿」。它永遠不會超賣。沒有任何打錯的關鍵字、編碼怪癖、或多出一個空白的 bug,能把一個鎖起來的連假日期變成可訂、然後讓客人為業主根本不想賣的那一晚刷了卡。已訂大於洽詢大於可訂,每一個模稜兩可都往下解讀。

那個詞,必須是螢幕上的那個詞

第一版用的關鍵字是描述業主「動作」的——一個鎖定指令。它改了三輪。最後定下來的版本,讓關鍵字跟客人在日曆格子上看到的標籤一模一樣。

這不是美觀問題。業主只要記一個字串,而他記它的方式,就是看自己的官網。他打進日曆的那個詞,就是印在格子上的那個詞。他腦袋裡不需要一張「我做的事」對「客人看到的東西」的翻譯表。一個詞、一個意思,就在他自然會去確認有沒有生效的那個地方。

文案本身也動了。一開始是只寫打電話的說明,後來放寬成「電話或 LINE 都歡迎」——LINE 才是這些客人真正找業主的方式。關鍵字跟著這個改動走,兩邊永遠不會分岔。螢幕上標籤寫什麼,業主就打什麼。標籤哪天再改,關鍵字跟著改,業主要記的還是眼前那個字。

視覺狀態自己撐得起來

可訂是灰綠。已訂是木紋斜紋。鎖定狀態是蜂蜜/琥珀色,加點狀邊框,字直接寫在格子裡——前景對背景大約 6.3:1,穩穩過 AA。顏色不是唯一在做事的:點狀邊框跟文字標籤都在標記狀態,所以一個分不出色相的客人還是讀得對。三種狀態,三種不靠顏色感知的編碼。

為什麼這是對的形狀

退一步看這個 diff。這裡的工程其實微不足道——一次字串比對、一個三值的 enum、一個 CSS 變數。讓它正確的,是每一個決定都往那個沒寫進規格的限制彎過去:操作這套系統的人是在經營民宿,不是在跑訂房平台,他只會用那個對他來說零額外學習成本的流程。

零新增日曆。零新增要打開的 App。一個詞,而且是他螢幕上已經有的那個詞。每一種失敗都導向安全的方向。這就是整個設計,也是它有機會真的被用起來的原因——而對一套全部價值都在於業主持續更新的系統來說,這是唯一算數的指標。

守不守得住,取決於摩擦感夠不夠低。這個賭注是:在一本他每天本來就會打開的日曆裡,打一個熟悉的詞,摩擦低到沒有理由不用。