diff --git a/src/lib/router-helpers.ts b/src/lib/router-helpers.ts
index 84bec63..f80f4ab 100644
--- a/src/lib/router-helpers.ts
+++ b/src/lib/router-helpers.ts
@@ -77,16 +77,3 @@ export const catchAllRoute: RouteRecordRaw = {
path: '/:pathMatch(.*)*',
redirect: '/',
}
-
-/**
- * Where to send the user after a successful login. Honours a `?redirect=`
- * query param so a page can bounce an anonymous user through /login and
- * back (e.g. chatelet's Book button). Only same-origin absolute paths are
- * accepted — anything else (external URLs, protocol-relative `//host`)
- * falls back to home.
- */
-export function postLoginTarget(redirect: unknown): string {
- if (typeof redirect !== 'string') return '/'
- if (!redirect.startsWith('/') || redirect.startsWith('//')) return '/'
- return redirect
-}
diff --git a/src/modules/chatelet/components/BookRoomDialog.vue b/src/modules/chatelet/components/BookRoomDialog.vue
deleted file mode 100644
index eccdb5d..0000000
--- a/src/modules/chatelet/components/BookRoomDialog.vue
+++ /dev/null
@@ -1,332 +0,0 @@
-
-
-
-
-
diff --git a/src/modules/chatelet/composables/useBookingFlow.ts b/src/modules/chatelet/composables/useBookingFlow.ts
deleted file mode 100644
index 26e49fa..0000000
--- a/src/modules/chatelet/composables/useBookingFlow.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import { computed, onUnmounted, ref } from 'vue'
-import { toast } from 'vue-sonner'
-import { SERVICE_TOKENS, tryInjectService } from '@/core/di-container'
-import type { PaymentService } from '@/core/services/PaymentService'
-import type { ChateletApiService } from '../services/ChateletApiService'
-import type { BookingQuote, BookingRequest } from '../types/booking'
-
-const POLL_MS = 2000
-
-/**
- * Drives one booking from request → invoice → paid. Modeled on events'
- * useTicketPurchase: the backend holds the dates and returns a bolt11;
- * this composable renders it (QR / copy / open-in-wallet / pay from the
- * LNbits wallet) and polls settlement until the invoice is paid. The
- * extension's paid-invoice listener is what confirms the booking
- * server-side — the guest only needs to see "paid" to know the dates
- * are theirs.
- */
-export function useBookingFlow() {
- // DI, never a direct import (workspace rule).
- const api = tryInjectService(SERVICE_TOKENS.CHATELET_API)
- const paymentService = tryInjectService(SERVICE_TOKENS.PAYMENT_SERVICE)
-
- const quote = ref(null)
- const qrCode = ref(null)
- const isRequesting = ref(false)
- const isPaymentPending = ref(false)
- const isPaid = ref(false)
- const copiedInvoice = ref(false)
-
- let pollTimer: ReturnType | null = null
-
- const paymentRequest = computed(() => quote.value?.payment_request ?? '')
- const booking = computed(() => quote.value?.booking ?? null)
-
- const userWallets = computed(() => paymentService?.userWallets ?? [])
- const hasWalletWithBalance = computed(() => paymentService?.hasWalletWithBalance ?? false)
- const isPayingWithWallet = computed(() => paymentService?.isProcessingPayment.value ?? false)
-
- /** Hold the dates and get the invoice; starts the settlement poll. */
- async function request(data: BookingRequest): Promise {
- if (!api) return null
- isRequesting.value = true
- try {
- const q = await api.requestBooking(data)
- quote.value = q
- try {
- qrCode.value = paymentService ? await paymentService.generateQRCode(q.payment_request) : null
- } catch (err) {
- console.error('Error generating QR code:', err)
- }
- startPolling(q.payment_hash)
- return q
- } finally {
- isRequesting.value = false
- }
- }
-
- function startPolling(hash: string) {
- stopPolling()
- isPaymentPending.value = true
-
- const check = async () => {
- try {
- const status = await api?.getPaymentStatus(hash)
- if (status?.paid) {
- isPaid.value = true
- stopPolling()
- }
- } catch (err) {
- // Transient network errors just mean "try again next tick".
- console.error('Error checking booking payment status:', err)
- }
- }
-
- void check()
- pollTimer = setInterval(check, POLL_MS)
- }
-
- function stopPolling() {
- if (pollTimer) {
- clearInterval(pollTimer)
- pollTimer = null
- }
- isPaymentPending.value = false
- }
-
- async function copyInvoice() {
- if (!paymentRequest.value) return
- try {
- await navigator.clipboard.writeText(paymentRequest.value)
- copiedInvoice.value = true
- setTimeout(() => (copiedInvoice.value = false), 1500)
- } catch {
- // Insecure context / old browser — "Open in wallet" still works.
- }
- }
-
- function openExternalWallet() {
- if (paymentRequest.value) paymentService?.openExternalWallet(paymentRequest.value)
- }
-
- /** Pay from the logged-in user's LNbits wallet; the poll picks up settlement. */
- async function payWithWallet() {
- if (!paymentService || !paymentRequest.value) return
- try {
- await paymentService.payWithWallet(paymentRequest.value, booking.value?.deposit_sat, {
- showToast: false,
- })
- } catch (err) {
- toast.error(err instanceof Error ? err.message : 'Wallet payment failed')
- }
- }
-
- function reset() {
- stopPolling()
- quote.value = null
- qrCode.value = null
- isPaid.value = false
- copiedInvoice.value = false
- }
-
- onUnmounted(stopPolling)
-
- return {
- quote,
- booking,
- paymentRequest,
- qrCode,
- isRequesting,
- isPaymentPending,
- isPaid,
- copiedInvoice,
- userWallets,
- hasWalletWithBalance,
- isPayingWithWallet,
- request,
- copyInvoice,
- openExternalWallet,
- payWithWallet,
- reset,
- }
-}
diff --git a/src/modules/chatelet/index.ts b/src/modules/chatelet/index.ts
index 8980b69..60653c9 100644
--- a/src/modules/chatelet/index.ts
+++ b/src/modules/chatelet/index.ts
@@ -8,7 +8,7 @@ export interface ChateletModuleConfig {
/**
* Chatelet module — guest booking UI for the LNbits `chatelet` room-rentals
- * extension: discovery, availability and Lightning-paid booking over HTTP.
+ * extension. Slice 1: read-only discovery + availability over HTTP.
*/
export const chateletModule = createModulePlugin({
name: 'chatelet',
@@ -52,4 +52,3 @@ export const chateletModule = createModulePlugin({
export default chateletModule
export type { Room, AvailabilityResult } from './types/room'
-export type { Booking, BookingQuote, BookingRequest, BookingStatus } from './types/booking'
diff --git a/src/modules/chatelet/services/ChateletApiService.ts b/src/modules/chatelet/services/ChateletApiService.ts
index c468c47..015252a 100644
--- a/src/modules/chatelet/services/ChateletApiService.ts
+++ b/src/modules/chatelet/services/ChateletApiService.ts
@@ -1,5 +1,4 @@
import type { AvailabilityResult, Room } from '../types/room'
-import type { BookingQuote, BookingRequest, PaymentStatus } from '../types/booking'
export interface ChateletApiConfig {
baseUrl: string
@@ -10,8 +9,8 @@ export interface ChateletApiConfig {
* HTTP client for the LNbits `chatelet` extension's guest surface.
*
* Plain config-wrapper (same pattern as events' TicketApiService) — not a
- * BaseService. Guest browsing, availability and the booking request are all
- * public endpoints; settlement is polled on the invoice via LNbits core.
+ * BaseService. Slice 1 is read-only guest browsing over public endpoints;
+ * booking (POST /bookings) + status polling arrive in slice 2.
*
* Transport note: this is HTTP-for-now. The extension also exposes a
* kind-21000 Nostr-RPC surface (chatelet_room_list / _availability / …), but
@@ -42,30 +41,6 @@ export class ChateletApiService {
})
}
- /**
- * Hold the dates and get the invoice that confirms them. The backend
- * quotes the amount (canonical `booking.deposit_sat`) — the guest never
- * recomputes what they owe. 502 = invoice creation failed server-side.
- */
- async requestBooking(data: BookingRequest): Promise {
- return this.request('/chatelet/api/v1/bookings', {
- method: 'POST',
- headers: { 'content-type': 'application/json' },
- body: JSON.stringify(data),
- })
- }
-
- /**
- * Anonymous settlement check on LNbits core. Chatelet's own
- * `GET /bookings/{id}` needs a wallet key, so until a public booking
- * status endpoint lands the guest polls the invoice instead — the
- * extension's paid-invoice listener is what flips the booking to
- * `confirmed`.
- */
- async getPaymentStatus(paymentHash: string): Promise {
- return this.request(`/api/v1/payments/${paymentHash}`, { method: 'GET' })
- }
-
private async request(path: string, init: RequestInit = {}): Promise {
const headers: Record = {
accept: 'application/json',
diff --git a/src/modules/chatelet/types/booking.ts b/src/modules/chatelet/types/booking.ts
deleted file mode 100644
index 8a843a2..0000000
--- a/src/modules/chatelet/types/booking.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * Booking wire types — mirror `models.py` in the LNbits `chatelet`
- * extension (BookingRequestData / Booking / BookingQuote). Field names are
- * kept as the backend spells them so the JSON maps 1:1.
- */
-
-export type BookingStatus =
- | 'held'
- | 'awaiting_payment'
- | 'confirmed'
- | 'declined'
- | 'cancelled'
- | 'expired'
- | (string & {})
-
-/** What a guest sends to `POST /chatelet/api/v1/bookings`. */
-export interface BookingRequest {
- room_id: string
- /** Hex pubkey of the guest — the identity the check-in DM goes to. */
- guest_pubkey: string
- /** YYYY-MM-DD, inclusive. */
- check_in: string
- /** YYYY-MM-DD, exclusive. */
- check_out: string
- num_guests: number
- guest_contact?: string
- message?: string
-}
-
-export interface Booking {
- id: string
- room_id: string
- guest_pubkey: string
- guest_contact: string | null
- check_in: string
- check_out: string
- nights: number
- num_guests: number
- /** Snapshot of the room's price currency. */
- currency: string
- /** nights × room price, display only. */
- price_fiat: number
- /** Canonical total due, in sats. */
- amount_sat: number
- /** Portion invoiced now (== amount_sat at 100 % prepay). */
- deposit_sat: number
- status: BookingStatus
- payment_hash: string | null
- /** Hold expiry while held / awaiting_payment; null once confirmed. */
- expires_at: string | null
- created_at: string
- updated_at: string
-}
-
-/** Response to a booking request: the held booking + the bolt11 that confirms it. */
-export interface BookingQuote {
- booking: Booking
- payment_request: string
- payment_hash: string
-}
-
-/**
- * Anonymous read of LNbits core `GET /api/v1/payments/{hash}` — the guest
- * cannot read their booking over HTTP yet (that endpoint needs a wallet
- * key), so settlement is polled on the invoice instead. `status` is only
- * present on a failed payment.
- */
-export interface PaymentStatus {
- paid: boolean
- status?: string
-}
diff --git a/src/modules/chatelet/views/ChateletDetailPage.vue b/src/modules/chatelet/views/ChateletDetailPage.vue
index ae43195..2000bdb 100644
--- a/src/modules/chatelet/views/ChateletDetailPage.vue
+++ b/src/modules/chatelet/views/ChateletDetailPage.vue
@@ -1,6 +1,6 @@
@@ -186,22 +138,12 @@ watch(showBooking, (open) => {
(≈ {{ result.quote_fiat }} {{ result.currency }})
-
+
Not available for those dates.
-
-
diff --git a/src/pages/Login.vue b/src/pages/Login.vue
index 7a440c7..ff89413 100644
--- a/src/pages/Login.vue
+++ b/src/pages/Login.vue
@@ -111,8 +111,7 @@