Files
hanzo.ai/hooks/useQuote.ts
zeekay 6c65e3e941 @hanzo/app 0.2.0: the app SDK, renamed from @hanzo/rooms
Every import, the catalog pin and the docs name @hanzo/app and
github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and
components/rooms.tsx is components/app.tsx.
2026-10-22 19:07:20 -07:00

37 lines
1.3 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { api } from '@hanzo/app/lib/api'
import { drop, kept, read, type Quote } from '@/lib/coupon'
/**
* Commerce's quote for a coupon code (lib/coupon): `undefined` while it is
* asked, `null` with no code. A code commerce refuses is dropped from this tab
* when it is the code the tab keeps; one it could not be asked about is said as
* such and kept. The answer is held
* with the code it was asked for, so a new code reads as asked until its own
* answer arrives.
*/
export function useQuote(code: string | null): Quote | null | undefined {
const [got, setGot] = useState<{ code: string; quote: Quote } | null>(null)
useEffect(() => {
if (!code) return
let live = true
const answer = (quote: Quote) => live && setGot({ code, quote })
fetch(`${api()}/v1/billing/plans?${new URLSearchParams({ coupon: code })}`, { cache: 'no-store' })
.then((res) => (res.ok ? res.json() : null))
.then((body: unknown) => {
const quote = read(body) ?? { refused: '' }
if ('refused' in quote && quote.refused && kept() === code) drop()
answer(quote)
})
.catch(() => answer({ refused: '' }))
return () => {
live = false
}
}, [code])
if (!code) return null
return got?.code === code ? got.quote : undefined
}